Live data from Hacker News

Giving up on test-first development

iansommerville.com

11–20 of 224 posts

Re: Giving up on test-first development

#11
post #5

From the article: Because you want to ensure that you always pass the majority of tests, you tend to think about this when you change and extend the program. You therefore are more reluctant to make large-scale changes that will lead to the failure of lots of tests. Psychologically, you become conservative to avoid breaking lots of tests. Interesting. I've often found that the lack of tests leaves me absolutely terri…

It's not a good argument against testing, I agree. Automated testing is a huge boon for software quality (though by no means a panacea).

But TDD, in my experience, gives a slightly different dynamic. Because you generated the code being motivated by the tests, there are a lot of unit tests that don't test functional units - tests that are essentially testing implementation decisions.

I've seen situations where that big refactoring would involve scrapping or rewriting tests, and that causes its own kind of architectural conservatism.

I ended up in a compromise position. I use a lot of tests, but I only do TDD in niche cases where it suits the problem I'm solving.

Re: Giving up on test-first development

#12
One of the things that people aim for in writing tests is orthogonality - different tests should not break for the same reason. This promotes the ability to refactor and change your code. I have also seen massive codebases, with masses of tests which were rarely run, and which effectively concreted the code and stopped it from changing.

Re: Giving up on test-first development

#13

TDD is nuts for code without a client or specification. The whole point of tests is to ensure that the code does what it's supposed to do. When you have neither client nor spec, how are you supposed to know what the code is supposed to do ? There is, IME, a >90% chance that any such code will be ripped out and replaced as you develop a better understanding of the problem domain. I've found it's pretty useful to go ba…

I don't agree with this at all as the tests at least say what the code does in its current state. I see that as incredibly useful. It also forces you to think about what it's actually doing as you build it. I get the OP's forest-for-the-trees thing, but I still find incredible value in testing things - even my side projects.

Re: Giving up on test-first development

#14

TDD is nuts for code without a client or specification. The whole point of tests is to ensure that the code does what it's supposed to do. When you have neither client nor spec, how are you supposed to know what the code is supposed to do ? There is, IME, a >90% chance that any such code will be ripped out and replaced as you develop a better understanding of the problem domain. I've found it's pretty useful to go ba…

If you don't know what the code is supposed to do, how are you writing it?

Re: Giving up on test-first development

#15
Over the years I've gone from writing no tests at all, to being a die hard TDD purist, and then out the other side to writing some things with tests first, others with tests after writing the code, and some without any tests at all.

In some situations I have clear view of what I need to build, and how that should work. TDD is great in that case - write a test for the expected behaviour, make it pass, refactor, rinse and repeat. The element I think a lot of people miss when doing this is higher level integration tests that ensure everything works together, because that's the hard bit, but its also essential.

Other situations you're still feeling out the problem space and don't necessarily know exactly what the solution is. There's an argument that in those cases you should find a solution with some exploratory development then throw it all out and do it with TDD. If I've got the time that's probably true, it'll result in better code simply through designing it the second time with the insight provided by the first pass, but often that just isn't viable. Deadlines loom, there's a bug that needs fixing elsewhere, and I've got two hours until the end of the week and a meal date with my wife.

Finally there's the times when tests just aren't needed, or they don't offer a decent return on the effort that will be required to make them work. I'm thinking GUIs, integration testing HTTP requests to other services, and intentionally non-deterministic code. Those cases certainly can be tested, but it often results in a much more abstract design than would otherwise be called for, and brittle tests. Brittle tests mean that eventually you stop paying attention to the test suite failing because its probably just a GUI test again, and that eventually leads to nasty bugs making it into production.

One thing I'll directly say on the article is that I found his opinion that its hard to write tests for handling bad data. That's almost the easiest thing to test, especially if you're finding bugs due to bad data in the real world - you take the bad data that caused the bug, reduce it down to a test case, then make the test pass. That process has been a huge boon in developing a data ingestion system for an e-commerce platform to import data from partner's websites as its simply a case of dumping the HTTP response to a file and then writing a test against it rather than having to hit the partner's website constantly.

Re: Giving up on test-first development

#16
post #6

Like all things in life TDD should be taken in moderation. It's an excellent process to create stable and maintainable code, but it does not fit every bill. But abandoning it completely on the grounds that it sometimes makes you write "bad software" is a bit weird to me, in fact, one of the main arguments for TDD is that it makes you write better code. I found that it does make you write better code many times. So li…

I like to think that, like with any other technique, with experience comes the ability to decide when not to apply it. Any old tutorial will show you an example of when it works, but only with experience will you learn when it might not.

Re: Giving up on test-first development

#17
post #5

From the article: Because you want to ensure that you always pass the majority of tests, you tend to think about this when you change and extend the program. You therefore are more reluctant to make large-scale changes that will lead to the failure of lots of tests. Psychologically, you become conservative to avoid breaking lots of tests. Interesting. I've often found that the lack of tests leaves me absolutely terri…

From what I understand author is not questioning the usefulness of testing, just TDD approach of writing tests first. I personally prefer a sandwich approach, I start with code first, and then write tests for it as soon as I have that little piece of logic finished (usually a method or two). Then I add more code, followed by more test, and so on. Works great for me and my team.

Re: Giving up on test-first development

#18
> ... it encourages you to make design decisions that can be tested rather than the right decisions for the program users...

This is exactly why I don't do TDD. I prefer to think about the design and implement features in non-distracting way. After I have it shaped, I add tests to avoid regressions.

Where I do write tests first is when I need to reproduce a bug, so I have the test, again, to prevent regressions.

Re: Giving up on test-first development

#19
post #5

From the article: Because you want to ensure that you always pass the majority of tests, you tend to think about this when you change and extend the program. You therefore are more reluctant to make large-scale changes that will lead to the failure of lots of tests. Psychologically, you become conservative to avoid breaking lots of tests. Interesting. I've often found that the lack of tests leaves me absolutely terri…

To be fair the author was abandoning TDD not tests. He explictly said that he'll add tests after he writes the code.

Perhaps the author is more disciplined than me, but I'd have an extremely hard time actually following through and writing tests after-the-fact. After the code is "done", it's hard to keep working on it—no matter how much the "extra" work is needed.

I find tests to be extremely helpful for designing APIs. I write tests to enforce the contract my API is making with its consumers. Therefore, TDD forces me to consider the API from the user's perspective, rather than designing an API based on implementation details. That also helps to cut down on the cost of refactoring—unless I'm making an API-incompatible change, my tests should keep passing. This gives me more confidence as I'm refactoring, allowing me to make more sweeping changes.

Re: Giving up on test-first development

#20
post #5

From the article: Because you want to ensure that you always pass the majority of tests, you tend to think about this when you change and extend the program. You therefore are more reluctant to make large-scale changes that will lead to the failure of lots of tests. Psychologically, you become conservative to avoid breaking lots of tests. Interesting. I've often found that the lack of tests leaves me absolutely terri…

One good way to the limit breakage in such cases is to solely perform black box tests on the API level. In case of our Node.js based Backends we don't ever write a single classical unit test, instead we have a custom Framework built on top of Mocha which performs tests on the HTTP layer against all of our endpoints.

This works remarkable well in practice and allows for large scale refactorings under the hood with little to no impact on the tests. We can also mock databases, memcached, redis and graylog on their respective http/tcp/udp level. This in turn means no custom build mocks which could break when refactoring. The tests itself also contain no logic, they are pretty much just chained method calls with data that should go in and an expected response that should come out, along with a specification of all external resource our API fetches during the request and their responses etc. Any unexpected outgoing HTTP request from our server will actually result in a test failure.

As for scaling this approach, from our experience it works quite well, especially when you have lots of complicated interactions with customer APIs during your requests since the flows are super quick to set up.

Post reply on HN