Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

TDD can be great if you know exactly what you're building and what you must test for, but it gets in the way when you're rapidly prototyping and exploring.

It sounds like you're making the mistake of thinking that TDD means writing the tests first. That's not quite right but it's understandable that lots of people believe it given the way TDD is usually discussed.

In TDD you should be writing one test first. Then you write enough code to make that test pass. Then add a second test. Get that to pass. And so on. If you realise you missed something you can delete tests that don't make sense any morejuat like you delete code that doesn't make sense. In essence TDD is about writing the tests in parallel to writing the code instead of writing them afterwards.

You can easily prototype and experiment with TDD because you're never much further ahead with your tests.

TDD is faster than writing tests later because your code has to be testable throughout the process. Writing tests later often means you need to refactor or rebuild to make it testable, which is a waste of effort, even in a prototype.



Kent Beck (yes, the Kent Beck): "I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence..." https://stackoverflow.com/questions/153234/how-deep-are-your...


I think what Kent is saying there is that he knows which tests he can skip writing because they provide little value. If you're as experienced and knowledgable as Kent then that's probably true.

For the rest of us it's better to write too many tests than too few. You might waste some time, but that a smaller problem than accidentally skipping a test that was actually useful and pushed your code in a better direction than you'd have gone without it.


> For the rest of us it's better to write too many tests than too few. You might waste some time

That's a very bad attitude that is all but enforced by TDD proponents.

It's very easy to see which tests are useless and which are not. So you end up with people writing thousands of unit tests and little-to-no integration or functional tests because:

- "TDD told me so", and

- Testing frameworks are written by people who adhere to the same philosophy

Whereas there's very little utility in those "too many tests" while they give you a very false sense of security.


It's very easy to see which tests are useless and which are not.

For trivial things, maybe, but then people start applying the 'rule' to non-trivial things "because it's easy" and they get it wrong. All I'm saying is that until you are a Kent Beck level expert it's safer to err on the side of caution. When you have a ton of experience and knowledge you can do what you like.

Also note that I said its better to write too many tests than too few - that still doesn't mean you should test everything, or that you have to test trivial things. It just means when you're not 100% sure it's safer to write a test.


> For trivial things, maybe, but then people start applying the 'rule' to non-trivial things "because it's easy" and they get it wrong. All I'm saying is that until you are a Kent Beck level expert it's safer

Most of the stuff we do is trivial [1]. Unfortunately no one really teaches testing or shows what needs ot be tested. Hence the prevalent "write hundreds of tests perhaps some of them will actually be useful". And most available examples, and most available advice is that: write many many useless tests.

I have a perfect example from the Java + Spring world. It's common to have a Controller + Service + Facade + external service client definition.

So I've seen countless times when tests are written as follows:

- external service client is mocked, multiple unit tests for the Facade to make sure it returns data

- Facade is mocked, multiple unit tests for Service, to make sure data is returned

- Service is mocked, mutiple unit tests for Controller, to make sure ata is returned

They are all the same tests, and can be easily deleted and replaced by a single suite of:

- external service is mocked, including invalid responses and timeouts. The actual rest service provided by the app is tested for all the scenarious that were quadruplicated in the unit tests above

You don't need to be a "kent Beck level expert" to do that. However, almost literally nothing teaches you to do that or helps you write those tests. Almost literally everything is hardwired to write small useless unit tests.

[1] Except UIs. I have no idea how to test UIs, and I don't think anyone does :D


> It sounds like you're making the mistake of thinking that TDD means writing the tests first.

Actually, Kent Beck, who coined the term TDD, has the following to say in his book "Test Driven Development":

"In Test-Driven Development, we Write new code only if an automated test has failed"

This implies that in TDD the test is written first.

Regardless of any definition, test-first and test-last are just two ends of a scale we can use.


He’s saying to write a test before its code, but don’t write all the tests before any code.


He's also saying "I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence..." https://stackoverflow.com/questions/153234/how-deep-are-your...


I have to disagree with that last statement.

The code I write initially can often be very dumb, repetitive and procedural. I often have to discover and see what the data structures really look like, what the public API looks like, what I leave in code and what I want to be data driven etc.

At this stage it makes little to no sense to write tests first. All I need is to see the output, which might change dramatically during development. It’s a very fast, direct way of programming, with a lot of copy pasting and long parameter lists.

When the code is laid out like that, then I can see where it’s going. I see the repetitions, the edge cases etc. Now it starts to make sense to design the data structures, give declarative names, find the right abstractions. Now I can move forward with tests.


>The code I write initially can often be very dumb, repetitive and procedural. I often have to discover and see what the data structures really look like, what the public API looks like, what I leave in code and what I want to be data driven etc.

I do this plenty after writing a new (high level integration) test.

Usually I have a fairly clear idea of what the public API I want should look like before I code. A test that calls that API makes a good test. I might tweak the way it is called when coding the implementation, but probably not by much. Underneath it the integration test doesn't care what kind of data structures you use and it provides a safety harness to let you discover at will.

>At this stage it makes little to no sense to write tests first. All I need is to see the output, which might change dramatically during development.

I frequently write the test first for this type of code - e.g. APIs that output JSON blobs or command line apps that output a wall of text.

Where I'm expecting the output to change I program the test to temporarily overwrite it with the results of the program, eyeball the result and commit it all together if it's correct.


That first phase is what I call “playing around”. A necessary phase. When you explore/play one shouldn’t test because one is learning.

Testing (aka evaluation) should happen when one knows the routine + outcome you want to evaluate that routine one.

Whether it’s code or improving your singing or learning in school. This generalizes to all of that, IMO


But what test do I write? What does the poorly documented 3rd party API return? Is the test failing because there's an edge case in the API or because my code is wrong?


The very first test can be a simple "Did my HTTP request receive a response?". Then you can build on "Does this HTTP response have this value I need".... etc

The way I have always gone about TDD is just that I am testing the code I am writing by running the test, not from the main entrypoint of the application. The things you would log and look for when you run the application you instead validate with an `assert()`. Then once you have finished developing, you do a single pass verification from main and you have both a test and a function written


Do you see how playing this game of writing iterative tests would actually provude slower learnings than prototyping a call to an API and receiving a result that would inform a series of tests that would be much more informed?

It's just as easy to be inefficient writing useless/bad tests than it is writing bad code. I think every developer should do TDD in their career, because it will make you think about the testability of the code. That said once you understand how to write testable code than I think it makes sense to be flexible on whether you write the test first or take a crack at an implementation.

The argument that you should have a good understanding of requirements I think leads to a lot of analysis paralysis, and isn't a practical method for building novel things in a timely manner.


I think the key distinction here is "run my new function through the entry point of the application" vs "run my new function from a test". You leave out whatever startup time is involved from the rest of the application to get to that function, & you have a tiny feedback loop to test the function.

You have your test which gets the response, and you can still log the output to see what it looks like. It's a little slower at first since you have to write the test, but I'm not sure I see a big distinction vs "have a small script that sends the request".


> Do you see how playing this game of writing iterative tests would actually provude slower learnings than prototyping a call to an API and receiving a result that would inform a series of tests that would be much more informed?

And do you see that in many examples just coding ahead without conaidering how to test the code later will in many cases leave you with code that is such a pain to test that you start avoiding the tests because "they take too much time".

If you start with the test the question of "how can this be made testable" is unavoidable.

I am not dogmatic here. If you are operating on the level of "I am happy if anything works at all" then starting with writing a test is probably a bad idea, better try things out first, scrap the whole thing once you understood what you wanna do in which way and then start writing the tests.

For a lot of adhoc code test driven development doesn't make too much sense, but if the stuff you write could ruin someones day (or life) maybe it is the way to go.


You start by allowing external requests from inside the test and automate your exploration of the API. Then you slowly evolve it to mock or stub the elements that interact with the external system so you can run your tests in isolation.


If you believe the answer to any of those questions is "So I shouldn't write tests, right?" then you must build some very flaky apps.


There is a difference between writing tests once you're done with your exploratory phase and know what you have to code up and doing full blown TDD from the beginning.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: