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

TBH, I don't know anyone who does TDD. I only know a few handfuls of developers, but so far the only people I've briefly met that do TDD were ones selling it; the consulting company that came into the company I worked for.

This consulting company tried to "teach" us TDD at the same time as I think they were learning it. Not a single developer out of the ~40 on our teams bought into it, because in almost all cases the code for a given example (during these 90 minute seminars) was always more confusing and obfuscated, and the unit tests didn't truly match the business case, because TDD is a bit like waterfall, in the sense that you're assuming you know what the code needs to do before you write it (how many times, especially for a large project, does your code change before you finish and are ready to write tests?).

After the 3 months of consulting, they left us and we continued on writing code and unit tests (in that order) just as we had before.

I think that TDD changes your code for the worse, except in very small, controlled cases. For e.g., a large web app, I think it just creates a mess.

If you think of all unit tests as regression tests, you'll have a good time.



I did a new "production" project where I did full TDD.

Ended up with about 200 tests.

As things go, specs changed and part of this application had to be rewritten. Most of the tests started to fail, not because the code was wrong, but because they tested for different spec. So I needed to rewrite all those unit tests again.

Sure, I catched "some" refactoring bugs, but most failed tests weren't bugs, just tests that became wrong. This made me question the whole TDD approach in production.


> TDD is a bit like waterfall, in the sense that you're assuming you know what the code needs to do before you write it

Very true; but there are cases where you actually have that knowledge. If you are asked to port some application to another framework or language, and have tests, I'd highly recommend you use them.

I have been refactoring an ETL pipeline (to offer extensible, more flexible behavior) that sort of grew out of interns applying ad hoc transient business rules. No tests were present but I had a quite large coverage over past inputs that were yielding a correct output. I'd say code size shrank about 70%.


> ... and have tests, I'd highly recommend you use them...

Absolutely, but that's not TDD.

The old tests (presumably) were written as regression tests, and code changes must comply to the (hence) regression tests' expectations. That's perfectly reasonable, and I think that's most tested software projects operate.


I somewhat agree. I think everyone should have to do TDD at least once, on a reasonably sized project. Thereafter, I'd trust them to pick and choose the important tests to write. TDD tends to result in a lot of unnecessary testing, but I've seen too many devs with no idea what should be tested, or how to write a good test, or how to write non-fragile tests, etc.

Note, the above is for things like web applications. For, say, hospital or billing code, TDD makes perfect sense.


I'm not sure I understand your perspective. If you have a specification (shouldn't a web app have a specification? Admittedly, I've never developed one, my world is embedded), then you have something to write your tests too.

While we don't use unit tests in my office, we do use comprehensive testing and a (mostly) TDD style (accidentally or coincidentally, our process has operated in roughly the same way for 30 years, mostly improvements to speed by way of automation).

We have a spec, we need our device to respond to message X with message Y after time delay T (or within time window [T1,T2]). We've constructed tests which verify these requirements. We write software that passes (or not) these tests. When we fail, we isolate the cause of the failure (sometimes we're dealing with several levels of hardware, not all under our control). Where the fault is ours, we fix it. Where it's not, we report it and wait a week for a fix (and test other things in the interim).

This message protocol is our API, the thing we need to test. If it were a web app, you'd have some set of URLs and GET/PUT requests that should cause some desired and measurable behavior.


> If you have a specification...

A specification doesn't translate directly into code. Otherwise, developers wouldn't have jobs. A specification is interpreted by developers, then reinterpreted by other developers, managers, etc., then updated, etc.


You can create your own specification from the stakeholders' requirements. If you can't create a specification, how do you define what you're programming in order to develop towards some goal?


Can you paste an example specification for a small program?

I use a whiteboard (or paper), as do many others, to diagram things out, as a means to put a program's abstract form into my brain. That whiteboard doesn't contain the entire program.


I don't have one at hand. The last one I did, a one-off for testing (contractor provided software had issues, we couldn't test because of that, wrote a subset of its features).

The specification can vary, but you still need a specification. In that case what I ended up with was an org file that detailed the timeline for the program (had 3 states for initialization, then went into execution state), the various tasks (embedded system, roughly comparable to threads or processes, but no parallelism potential, more a logical breakdown of the program structure).

I had a set of messages described as potential inputs, a set of messages described as responses. Another set of messages that I sent periodically (description included when they were to be sent and what their contents were supposed to be).

In this case, like I said before, we didn't have unit tests. It was more equivalent to integration tests. But we used a subset of our test suite to verify that (with this fake-driver in place) the software we were developing was properly sending and receiving inputs. So we were able to reuse existing testing infrastructure. Also, in this case, every test "failed". But the portions of it that we cared about passed (well enough, my messages weren't as dynamic as the real thing so the failures were all expected failures).

The org file was, more or less, like this:

  * Driver [/]
  ** TODO States [/]
      - [ ] Initialize - INIT
        Configure shared memory so other applications can access it
      - [ ] Wait for core - CORE_WAIT
        Wait for core to write value X to location Y.
      - [ ] Wait for app - APP_WAIT
        Wait for app to reach ready state
  ** TODO CORE_WAIT [/]
      - [ ] Some details on what we actually cared about here
  ** TODO Input Messages [/]
      - [ ] Message X
        Details on format, later turned into a C struct
      - [ ] Message Y
        Details on how it should change the driver state
      - [ ] Message Z
        Details on how we should respond (with Messages A, B, C)
        - [[MSG_A]] is used when Z.field = 0 
        - [[MSG_B]] is used when Z.field = 1
  ** TODO Output Messages [/]
  *** TODO Responses [/]
      - [ ] <<MSG_A>> Message A
        Sent in response to Z if Z.field = 0
      - [ ] <<MSG_B>> Message B
        Sent in response to Z if Z.field = 1
  *** TODO Periodic (not responses) [/]
      - [ ] <<MSG_D>> Message D
That's a specification (with details removed). This is how I generally write things. Start off with an outline, fill it in. Create links to other areas of the specification, resources like message specification (ours is a very domain-specific one, but others might just be RFCs on more standard or common protocols), language or library documentation, with org-mode you can link directly to source code (I don't utilize this as much as I'd like). A side benefit here is that I can tie it in with my task management system. And tracking progress is automatically done (the [/] bits will tally the number of completed tasks and the total, in emacs this is also colored red/green (default) so I can quickly see what's left).

Want a diagram, that's fine. In my office that means a visio document. I'd make that and link to the file as well. I actually did, in this case, link to a few provided diagrams. Perhaps instead of the detailed text on the message format, I link directly to a specification for message formats. I sometimes write one-off programs to test out the provided libraries or test my actual understanding of the hardware we're running on (useful, because it's often not compliant to the specification, still under development). I almost always (I'd say 80%) write my code like this these days (last 2 years in particular, less consistent before that, and before that I was a tester). Any program that's more than 50 lines of (typically) C, ends up getting this treatment.

I recognize that it's somewhat easier in my primary domain (embedded systems). We generally deal with smaller programs (< 30k SLOC of C), usually know exactly what the inputs and outputs ought to be. In other domains you have to be more flexible. But this structure isn't inherently rigid. Providing a specification, even if it's piecemeal, is better than not as it gives you something to measure against.




Consider applying for YC's Winter 2027 batch! Applications are open till November 2.

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

Search: