I gained a new appreciation for Test Driven Development
worthe-it.co.za
worthe-it.co.za
Just don't be dogmatic about practices like TDD. It's always a tradeoff. Being a good engineer means understanding when the tradeoff is worth it and when it isn't. Be wary of development teams, or people, that force you to practice certain methodologies regardless of context.
TDD comes out of the same circles as Extreme Programming, aka XP. In XP, there's something known as a spike, which is basically a short-lived technical prototype. If you really don't know how to tackle something, you build a quick one to throw away.
In that case, yes, definitely don't do TDD, because a goal of TDD is to build up a good test suite for production code. It's not a good match for throwaway code (assuming the team really has the discipline to throw it away.)
But if I'm exploring in a way that feels less disposable to me, which happens often, then I'm happy to use TDD. E.g., if I want to try out a new way of solving a problem, or I have an idea for an architectural improvement. Then I'll use TDD to force myself to think about it from the outside in, to keep design considerations to the forefront.
But you should know this from your functional requirements, right?
(Slightly sarcastic comment, I know in a lot of places people just start coding without a plan, but I think TDD and functional requirements go together well).
Well, I don't do that. But i usually don't know all the names of my (internal) interfaces and models and properties right at the start. I might move things around and rename stuff along the way. And I think that's quite normal if you don't live in a world where the spec for a small task is multiple books thick.
Doesn't TDD get in the way with? Wouldn't I keep refactoring the tests all the time then?
Functional requirements describe observable behaviour. You can't write any meaningful tests for that behaviour until a lot of your code is written.
Yet somehow in all the discussions there's this unexplaned leap from "you have the requirements" -> "well you just write a unit test for your function first, and then code it".
My functional requirements come in the form of "Given publisher A in Country B and contract requirements C the service must return D for a subset of products E", for multiple combinations of A, B, C, D, and E. And where each of those may and will require lookups in external services and databases.
Essentially if you don't have an existing code structure in place, TDD will be very confusing and you'll end up tangling yourself into a coding mess.
On the contrary, if you have a structure in place and need to ensure the correctness of the code you're about to write, it is incredibly useful.
Building my own stuff, I hardly ever write any tests tho as in the early stages of a project, testing will slow you down as the tests are essentially locking your code in. For me personally, I prefer to have my code be more fluid so I can rapidly change it without having the overhead of having to write and rewrite the corresponding tests.
When I come across some tricky logic tho I will definitely crank out some tests as it actually helps to create the solution.
As usual context is key to knowing whether TDD (or even testing) is appropriate or not.
If you prototype a new features for an existing system, a test let you execute only the code you actually need. This will shorten your feedback loop and allow you to iterate faster. Refactoring a test is fine. Writing a BS test just to explore a solution is fine. In my experience following a test lead practice will help you build a simpler system which will be easier to maintain.
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.
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
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.
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".
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.
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.
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.
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.
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
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.
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.
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
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.
In fact, if you know exactly what you are building, how could tests drive your decisions? You have already made up your mind. If you know what you are building you can simply implement it straight away without the data gathering phase.
This is the main issue that I have with TDD. I use a methodology that I call "Evolutionary Design,"[0] and TDD won't work for that.
But I think they have the right idea, in emphasizing the need for extreme Quality. I like to achieve this, by using test harnesses, as opposed to unit tests[1].
[0] https://littlegreenviper.com/various/evolutionary-design-spe...
[1] https://littlegreenviper.com/various/testing-harness-vs-unit...
However well written tests are independent of how you organize your code or any other implementation details. So tests are great even when you are just prototyping or exploring. You can rapidly change your code and have the tests ensure that it still works the same way. And the end result is a fully tested, easy to maintain code base. What’s not to love about that?
And people always ask if it's really true every time I say that.
What I've found is that you need to reduce the friction to writing tests, if you want them to get written. Once it's easier to write automated tests than to test manually, the benefits are a lot more clearer.
Manual testing has a much higher, but hidden, cost.
If the friction is higher, tests will be forgotten amid the pressure from stakeholders and deadlines.
Once you discover the benefits of low friction automated tests and write testable code by default, even you don't practice TDD by the book, you can get some of the benefits mentally.
And if you are using static typing, the amount of tests you need write goes down significantly.
Though, typing that last part out. Maybe the concern is over the level of the tests. Unit tests are surprisingly easy to understand based on how you have divided out the code. However, there is very little that is required on how you have done this division. Path dependence is a huge thing to define what divisions of code you will end up with. Such that I have a hard time seeing how that works in a TDD fashion.
Before I write a line of code, test or no, I usually have some idea of what the code should do. Then I run the code to see if it does what I want. So if I'm not doing TDD, I'm still doing "manual test test first"; the test is just in my head.
TDD is just saying, "Let's start by automating a bit of that test and make the test fails correctly. Then we write production code until the test passes. Once it goes green, we look at what we've got, see how we like it, and maybe refactor a bit before writing the next test."
If I'm totally off the map and flailing around, yeah, I won't write tests first. I'll hack at something until I have a clue. And then I'll throw that code away and write my first test. Which is something I should do regardless of whether I'm doing TDD, as scratch code like that is too messy to keep. Easier to dump it and start fresh.
And, to be fully fair, I'm sure this is in many walks. Cast iron advocates. Electric stoves. Manual transmission vehicles.
The worst is when things /used/ to be true. The world has a habit of moving on.
My guidance would be to not start with many tests when writing a prototype / initial development of something - just write one or two of the most basic things you’re confident you’ll need. As your finished product comes into focus, then you can add more tests.
I don’t literally write a test before every line of code, yet I still consider what I do to mostly be TDD.
Writing too many tests feels like getting ahead of myself. It feels like a bet that I won't learn anything or think of anything new as I go. Which is a bet I don't like making, because it often becomes self-fulfilling.
Why would you nee to know what tests are needed? Just about any bit of functionality you can come up with to implement you should be able to come up with a test to test it. It's not really harder to know what code to implement than it is to know what test to exercise that implementation.
And at any point in time you only need to think of a single test before you're back to coding. It's not like you need to plan out 50 tests up front. Whatever the next handful of lines of code you're going to write write a test for that first, then write those same lines. Then pick the next handful via test, then write them.
I don't understand the difference in knowledge at all.
It is reasonable to think you can know what functionality you need to test. And, for certain, if you know the boundaries of the system you are building, you will know some. That said, there is a tendency to build more boundaries than needed in our industry, period. Would be like a community garden that thought they should arrange things by type of plant that will be created by the community. When the actual divisions should be on how many community members you will have.
It's not the best example. It's the one well worn example that everyone pulls out and is a perfect case of letting perfect be the enemy of good.
Something works for 99% of cases, someone finds a 1% case it doesn't work for an says "ok I don't need to evaluate it it's useless".
Not tool works 100% of the time, the fact that it didn't work for someone creating a constraint solver is effectively irrelevant.
That said, I'm more than game for alternative examples. Have any that show benefits? Most examples I ever see fall, at best, into drawing the rest of the owl.
My requirements are in terms of observable program behaviour, and that is what needs testing. Not the hindreds/thousands of tiny one-off tests in between. And... you can't test that observable behaviour with TDD until you've written a solid chunk in between.
No. I usually write functional/integration tests after I've written most of the code.
> TDD promotes that you start with just one.
And that one will be absolutely entirely useless. Because what would be that "first one test"?
Let's assume it's a REST or GraphQL service that returns aggregated data from multiple external services. Until you have more-or-less functioning code (including all model definitions, client definitions, data transforms etc.) you can't even write a "first test for observable behaviour". Also here: https://news.ycombinator.com/item?id=34765360
Whatever you want. Just pick some small attribute that you expect to observe.
I'd personally start with failure modes as they are the most important and interesting code you will write. Hell, you can likely not even bother testing the so-called "happy path" because, really, who cares what happens during success? Failure is where you want users and future readers to have the most documentation to ensure that when things go wrong the metaphorical bridge doesn't come crashing down into the river but rather remains in a repairable state.
If it's a REST endpoint that aggregates data from multiple external services, you probably want to see a "retry after" response sent to the client if any of those external services are temporarily unavailable. So write a test that asserts that, then write code that does it. This is also a nice first case as you can detect an unavailable external service quite early in your pipeline, requiring very little of the implementation to see the test pass.
No need for religion. If documenting your code later works for you, go for it. Nobody cares. Tests won't be driving your development, so it won't be TDD, but it'll be something and that may be enough. However, in reality most people simply don't have the wherewithal to plan for things like being able to test unavailable remote services if they don't have a test case in front of them forcing them to and won't bother when it is too hard to add later, so there is something to be said about the TDD approach as a general rule, deviating only after you fully understand the tradeoffs.
The expected "attribute" is correct data being returned.
> Hell, you can likely not even bother testing the so-called "happy path" because, really, who cares what happens during success?
wat?
> If it's a REST endpoint that aggregates data from multiple external services, you probably want to see a "retry after" response sent to the client if any of those external services are temporarily unavailable. So write a test that asserts that, then write code that does it.
So again you want me to write a meaningless test that will be duplicated anyway during the actual proper test. See https://news.ycombinator.com/item?id=34765360
> However, in reality most people simply don't have the wherewithal to plan for things like being able to test unavailable remote services if they don't have a test case in front of them forcing them
This is a weird statement that is definitely not backed up by reality.
> there is something to be said about the TDD approach as a general rule, deviating only after you fully understand the tradeoffs.
The problem is, everyone advocates writing multiple useless tests, including TDD proponents. You can't learn "tradeoffs" until you look past the dogma. Once you've looked past the dogma these "tradeoffs" most of them are not tradeoffs, but trivial things that you would do anyway.
Even in the simple example given, there are probably hundreds of different failure cases to account for. The "correct data being returned" will never be a single attribute unless your application does effectively nothing.
> wat?
The successful case is usually the most understood behaviour, most visible, and easiest to recreate. If you are going to skimp out on documenting some aspects of your code because you hate future developers, the most understood aspect is where you are best to skimp.
> So again you want me to write a meaningless test that will be duplicated anyway during the actual proper test.
No. Why would you write the same documentation over and over again? This makes no sense.
> This is a weird statement that is definitely not backed up by reality.
It is certainly backed up every time I have worked with developers who put no effort into making their code testable, making it way harder than it should be to test certain cases afterwards. And in my experience they usually just throw their hands up in the air and say "testing takes too long" after they've made it unnecessarily hard to test.
Is it that you always work alone? If so, I can see why you think documentation isn't that useful. If you have a decent memory, it probably isn't. But most people have to work in teams or are in positions where they will one day be replaced and aren't writing documentation for just for themselves.
After all, that's what TDD – and, really, testing in general – is all about: Documentation.
> The problem is, everyone advocates writing multiple useless tests
Nobody advocates writing useless tests. Future readers should be able to learn something useful from every documentation item you write. That your function will return "retry after" when an upstream service is temporarily unavailable is very useful information for users and future developers to know. This is worth documenting.
You will undoubtedly need to write multiple tests. Nobody wants to read documentation that is one big ball of mud. A sane developer will break different cases into different tests to help future readers clearly understand that there are different environmental cases, like the range of failure states, to consider.
Definitely not hundreds. But yes, they need to be tested
> The successful case is usually the most understood behaviour, most visible, and easiest to recreate.
Tests are not written to document behaviour, but to validate that your program does what it's supposed to do. If you don't test successful cases, you don't even know if it runs correctly.
> If you are going to skimp out on documenting some aspects of your code because you hate future developers, the most understood aspect is where you are best to skimp.
I see you have the erroneous assumption that tests are documentation.
> It is certainly backed up every time I have worked with developers who put no effort into making their code testable, making it way harder than it should be to test certain cases afterwards.
This is slightly orthogonal to being able to write testable code etc. Most tests are trash to begin with, and most testing frameworks give next to zero help in designing and running actual tests you would really want.
I couldn't care less if a team mate wrote "untestable code" in some modules. In 99% of the time that code is some repetitive boilerplate anyway. Ironically, this is the code that everyone prioritises to make testable.
And then you want to test all that in concert... Ahahaha good luck. Even starting up a local rest service with mocked dependencies to test is a royal pain in the butt in most cases, and has nothing to do with testability of some small pieces of code.
> that's what TDD – and, really, testing in general – is all about: Documentation.
Tests are not documentation, have never been, and can never be viewed as such.
Testing is literally about testing: to test that your program behaves correctly given some requirements. It literally is in the name. You write tests not to educate your co-workers, but to make sure your program doesn't crash and burn the moment you push it to prod.
> Nobody advocates writing useless tests. Future readers should be able to learn something useful from every documentation item you write.
Again:
1. Tests are not documentation. Have never been, will never be.
2. Existing testing practices all but enforce writing useless tests, prioritising duplication, hundreds of unit tests etc. over what' actually needed.
> Nobody wants to read documentation that is one big ball of mud.
All tests are inevitably a ball of mud because they lack context, and they are not documentation.
Most likely hundreds. Each individual external service will easily have tens of conditions (a multitude of invalid input states, a multitude of malformed response payload states, a multitude of expected upstream error conditions, network interruption, port exhaustion, expired/invalid certificates, failed authentication/authorization, timeouts, etc., etc.) and when multiplied across them it won't be long before you have hundreds.
> Tests are not written to document behaviour, but to validate that your program does what it's supposed to do
A common misconception, but no. Tests would serve no purpose if you had no reason to document expected behaviour. Correct that tests also allow the machine to validate that what the documentation says is true, solving for the problems of the past where documentation and implementation would regularly fall out of sync. That is, indeed, the value proposition over writing the same information in Word instead.
Your documentation doesn't end there, of course, just as the documentation provided by a static type system is not the be all and end all of your documentation. But documentation it most certainly is.
> Existing testing practices all but enforce writing useless tests
The invented practices you have presented throughout the discussion no doubt lead to writing useless tests. It is, however, not clear why you hang on this invention of yours – outright ignoring what you are being given. Is it some kind of defence mechanism to protect the methods you have developed for yourself?
Not likely
> Each individual external service will easily have tens of conditions
You will likely not propagate those to the end user, but log and return a single error for many of those cases
> A common misconception, but no.
A common misconception is that tests are documentation in any shape or form.
> Tests would serve no purpose if you had no reason to document expected behaviour.
You do not document behaviour with tests. You write the tests to make sure your app conforms to documented behaviour.
As documentation tests are useless because they are a collection of separate and disparate scenarios and cases lacking any context or continuity. If anyone ends up looking at tests for documentation on how your app or service behaves, I have bad news for you.
> Your documentation doesn't end there
Thts is literally the dead end of documentation. Well, the actual dead end is "code is documentation" and "types are documentation". Neither of them are. Documentation is documentation. Comments are documntation. Design docs and specs are documentation.
Tests, and especially the way most practices propose tests should be written, could not be farther from documentation.
> The invented practices you have presented throughout the discussion no doubt lead to writing useless tests.
Indeed they do.
> It is, however, not clear why you hang on this invention of yours – outright ignoring what you are being given.
So you have fabricated this idea that I am ignoring something and is now attempting to make me defend this fabrication of yours.
Quite likely given the exact example we speak of. Not likely for all cases.
> You will likely not propagate those to the end user, but log and return a single error for many of those cases
Agreed. But your code still has to get there and, unless you've given up on testing, test that each of those potentially hundreds of error states leads to the single error response with the appropriate log that you expect.
If you don't put any thought into testing upfront, this is where you can quickly end up with a mess that does really take too long to add testing to. And let's face it, those without much testing experience don't know much about what could be done to not create such a testing nightmare. I see it time and time again.
No need for religion. Do whatever you want and if you understand the tradeoffs are no doubt you are better off for it. But a lot of developers without much experience don't understand the tradeoffs.
> You write the tests to make sure your app conforms to documented behaviour.
Yes, exactly. And the document that documents said behaviour is.... Your tests. Not a bad practice to have additional documentation on top, but the tests will ultimately be your source of truth. They serve as the documentation that the code is tested against.
> Tests, and especially the way most practices propose tests should be written, could not be farther from documentation.
Go on. Are you creating your own unusual definition up on the spot here for the word documentation or is there something more profound in here?
> So you have fabricated this idea that I am ignoring something and is now attempting to make me defend this fabrication of yours.
I have observed this idea that you are ignoring what I've written. It's right there. I write something, you immediately reject it without any curiosity and then go off on some unrelated tangent that has nothing to do with what was said to justify your rejection. It is quite curious.
It could be that you simply don't understand, but normally when people don't understand they want to learn. Education is usually considered a desirable quality. Which left me wondering if this behaviour is some kind of defence mechanism to see that you don't gain any insights into other ways of working to protect what you feel is best?
It's all very well saying that good architecture will emerge from TDD, but there's no way that's true unless the programmer has a good idea of where they are going in the first place.
TDD is most useful when you don't know what you are getting into. It allows you to quickly run small experiments against your hypotheses to see if the fuzzy thoughts running around your head are useful or need to be thrown out. The data gained from those experiments is what drives your development decisions.
If you already have everything already figured out, what more data do you need?
TDD assume that you can write a test as the first step. Which sort of sounds reasonable if you don't think about it too much. Sometimes it is reasonable (e.g. leetcode, improving existing code, well defined problems).
But a lot of the time that is sneakily missing out the enormous steps of prototyping, experimenting, trying solutions that didn't turn out to be a good idea, finding out that the problem wasn't a good idea, etc. etc.
All that can take longer than actually implementing a solution.
So, fine use TDD where it works well but don't pretend it is the only way to do things.
I also have two young kids and can absolutely relate to the author.
Not as big as the context switch that is between code and browsing the GUI of your app.
TDD gives you a sense of control. You know that you are done once your tests (read: documented requirements) pass.
And yes, I realize not doing TDD is not the same as not doing tests at all, but writing tests post-fact is just an excuse to wiggle out of writing them. "It worked when I coded it!"
But once it's working, it's the opposite of overwhelming. The red-green-refactor cycle of TDD lets you take work in very small chunks. And because you get great test coverage as part of it, there are way fewer landmines. For me at least, TDD in a good code base is the most soothing and productive way to work I've found so far.
But make no mistake - it's, requires discipline, and good software architecture. Which is my hot take on why it has so much blowback.
(Assuming that's what you meant by 'exceptional')
For example the claim that TDD impairs your ability to modify code is completely the opposite of my experience. It’s quite simple, just don’t write overly specific tests, and test your module interfaces (however your language implements them) rather than the internals. One code smell is that if your tests are getting in the way of refactoring you’re certainly doing it wrong, since refactoring is a key and I would say even defining part of the TDD development process.
Once you have found a solution that works for your context, you can switch to engineering. That's where TDD is used.
But the problem is that if you _only_ do TDD then you will be stuck being a poor programmer, because you will never develop the mental skills to design and hold code structures in your head beyond the trivial size. Beyond a certain point of maturity in your professional career you will just have to move past that, and then you will realize you have no use for TDD in 99% of the cases, it doesn't help you, in fact it slows you down and makes your code quality poorer.
I think there are 2 arguments for TDD:
1. For at least some bits of your code, you would benefit from having tests. Writing tests after the fact is a pain, so might as well start with them.
2. Even for highly skilled programmers who enjoy thinking about their code deeply as they write it, there is still a class of difficult algorithms where they would benefit by writing the tests first. I'm thinking about stuff like tricky graph algorithms or scientific computations etc.
That being said, I generally have found TDD to be too onerous - except for maybe 1% of the time when I do find the extra safety brought by the tests to be useful.
If there’s one thing I’ve learned after decades in the industry, it’s that we are all poor programmers. Our squishy wet human brains are simply not a good match for what computers do, and as such, we should take all the help we can get :)
Once you're dealing with multiple systems, or outputs dependant on the combination of an input and a complex state, unit tests aren't as easy to write or as useful.
It's more about what tests you write, when to write and how to write it in the most efficient way as possible.
So it was for me indeed also triggered by constraints to significantly improve my productivity
It's basically the same deal as cooking. Can I go faster if I dirty all the pans and don't worry about cleaning up? For a little while! And after that, productivity and quality drop. There's a reason that professional chefs are big on "working clean". For those interested, there's a book with a lot good interviews with chefs on their work practices. I think a lot of it translates to software: https://www.workclean.com/
I highly recommend writing automated tests to guide what you do even for small companies. Perhaps especially for resource constrained ones, when every hour spent working is hard earned.
> Specifically, it's a test first methodology, where […]
I am very confused by now, since I've heard one vocal camp saying TDD=TFD, and another vocal camp saying it most definitely is not, and that TFD is in fact the black sheep of TDD and should mostly be avoided.
What do people here think? (and if TDD is not necessarily TFD, then what is it exactly?)
Tests are great and TDD is great for some people, but the whole point is to write useful software. It's important not to lose sight of that.
They may have negative value, but it is not a foregone conclusion. Tests are merely documentation and that documentation very well could provide positive value for someone who reads it. There is no doubt insights to be gained in reading about what someone was once thinking about a particular problem. If well thought out, you might even begin implementation based on that work, saving time having to document it all over again.