3 ms·
Taming a Wild, Testless Code Beast
- npsimons 12y agoWhile most of these seem like common sense, it's surprising how few people actually practice these. Might seem repetitive and "I've already seen this" to some, but I hope many will learn something new (and implement it!) from this article. I can't recommend strongly enough the practices put forth in this article.
- GarethX 12y agoI liked the first section in particular. They may be obvious steps, but sometimes when you have the 'where the hell do I start' feeling, it's useful to just have some solid steps laid out.
- jader201 12y agoNice primer, but it omits a crucial part of "taming a testless code beast": Making it testable. Before you even get to step 1, chances are that if you already have a "testless beast", it's not testable. That is, the right abstractions aren't in place to be able to properly mock or fake your dependencies. To me, this is one of -- if not the -- hardest part(s) of adding tests to an already existing codebase. This is particularly true when dealing with a statically-typed language. This is why you'll often read how important it is to start an application with testing in mind: as you write your tests, you'll be forced to make your app testable. Otherwise, it's extremely easy to forego some of the necessary abstractions needed for testing -- and conversely, extremely hard to make sure you have all the necessary abstrations in place if you don't include testing from the beginning. So this article shouldn't be read thinking that it's "that simple" -- the hardest part isn't even mentioned here.
- wdewind 12y agoAs far as tests are a descriptor of the business logic, another really difficult part that is also omitted from this article actually figuring out who the person in the organization that can authoritatively tell you what the business logic is can be really tricky. Sometimes that person no longer even exists. Simply writing tests that test that the existing code does what it does is not always productive.
- ionrock 12y agoIt is really a chicken and egg problem. On the one hand, it would be good to refactor the code to help write some tests. On the other hand, you really shouldn't refactor the code with tests to prove that you haven't broke anything. In either case, you are correct that it can be extremely difficult. While there is no easy answer, I've found that in these situations, I still try to write a test first. The difference being that I try to write a test that exercises the code at some API level that I don't want to break. These test start off really ugly and require a ton of work, but they ensure that refactoring the lower level code to make it more testable doesn't break things. Eventually, the tests and code at the higher level can be updated safely.
- masklinn 12y agoI hope people don't expect it to be "that simple", there's at least one whole book on the subject ("working effectively with legacy code") and it though it stands on its own, it builds on predecessors (Refactoring)
- npsimons 12y agoIn the past two days I've dealt with just such a beast, and the answer is simple: start with just one, whole system test. If you've got an output file which is "known good", it can be as simple as running the program and doing a diff between the "known good" and the output, then start making changes. Automate this test, then start refactoring mercilessly, adding tests as you go.
- bayesianhorse 12y agoEvery wild testless code beast was once someone's pet.
- vinceguidry 12y agoWe have just such an app. Rails 2.3.8, (won't even upgrade to .9 cleanly) EOL Debian Lenny. No tests. At all. Started to do this. Figured out how to get it under test, when bugs are fixed they get tested. Every once in awhile I'd grab the most important piece of the project and put it under test. Gave up after awhile. It's a poor use of time. The app's been maintained for years, its failure modes are well-understood. It's stable. There are other parts of the infrastructure that need attention. I managed to convince my superiors that now's the time to replace it. Rather than devote countless hours to this time sink, I decided to stop adding to the legacy app, cordoned it off and now treat it like a Superfund site. I replaced my work machine a while back, I keep the old machine around because it's the only thing I can work on it with. I feel like I'm putting an environmental suit and gloves on every time I open it up. All new functionality is built outside the app and interacts with it as if it were a black box. We now have an agenda and a plan to replace it. And the projects I'm working on now will bring a lot more value and capability. We even got them to expand my department. What I'm saying is that organizational problems shouldn't be treated like technical problems. A large, untested, legacy application that a lot of business is riding on is an organizational problem and not a technical one. They decided not to devote the resources to properly maintain it, you need to put forth the case that it's a serious problem and that it needs more than your efforts to fix. That's how organizational problems are solved, one person takes the initiative and gets people behind him and makes it happen. Not by being a cowboy. Trying to take code that was written in one era, and bring it into another era is a huge waste of time and resources. Figure out what you need to really solve the problem and ask for the resources to do so, after you've gotten your ducks in a row and at least made efforts to do it other ways. Your career and your sanity will be better off for it.
- freework 12y agoThis post makes the mistake of thinking that writing a bunch of tests will make everything better. In my experience, the only thing that will help you when it comes to expanding/fixing an existing codebase is knowin what the code is doing. When the app crashes and you look at the traceback, you need your brain to be able to understand why the program crashed. No matter how many tests fail or how many email alerts you get from travis-ci, your primary task is to understand what the code is doing.
- npsimons 12y agoAnd how do you know what the code is doing? One of the most effective ways I've found is to look at the tests. If there are none, even better: I can get a really good feel for how the system operates by writing tests, increasing coverage as I go. Tests aren't an end; they are a means to assuring proper function, but also towards understanding and writing better software (by forcing you to make code testable, which naturally leads to decoupling, etc).