Yup, writing tests helps me sleep at night. TDD helps me manage my mental resources and iterate.
(another reason is communication--we code for our colleagues first, then for the machine: https://sonnet.io/posts/code-sober-debug-drunk/)
IIRC smalltalk had a workflow where you'd debug and write your program at the same time. You'd just reach a path that has not been implemented yet, break, implement it and continue.
This has been implemented to varying degrees of reliability, granularity (in terms of preserving state) and utility. It's a common concept but I don't know the name for it!
Examples:
https://en.wikipedia.org/wiki/Continuation
https://en.wikipedia.org/wiki/Fault_tolerance
Maybe fault tolerance is the right word? But it doesn't imply that you actually modify and build the thing while it already runs.
An example of fail open design in the real world, is electronic magnetic locks at apartment complexes or business buildings. If the building loses power the doors demagnetize and unlock, then indefinitely remains open but failing because it is always open now. The opposite would be the building loses power and the doors fail close and lock the doors indefinitely when power is lost, and now people can’t escape.
a good read is ROC https://en.m.wikipedia.org/wiki/Recovery-oriented_computing
This is exactly what I do with the PyCharm - hit a breakpoint, write the code as it should be, and execute it in the debug REPL to do basic initial testing [repeat]. Extremely productive.
> that the way to solve it, is to do absolutely no new features for a quarter or two, refactor the code, learn new methodologies
Even in a world without shareholders, we still are building things for users. 6 months without improvements has an effect on them, too. When possible, I think it's better to spread cleanup work out. Even if one spends 80% of the time on cleanup and 20% on features, that is much better for relationships than going dark for a quarter or two. And in my experience, continuing to do productive work during that period makes the behind-the-scenes improvements better.
They did exactly what you suggested! The relationship with the users never went dark. They kept up with bug fixes, and kept feeding new features at a more than acceptable pace. [Edit: Here's the pace of their status updates, as evidence at https://www.reddit.com/r/factorio/?f=flair_name%3A%22FFF%22 ]
I didn't quite believe that Uncle Bob's lessons worked in the real world, but if the Wube team is sold on them, that's about as good as an endorsement as I'll ever get.
I keep thinking I should try Factorio. But given amount of time friends have spent on it, I'm scared I'll never be heard from again. One of my friends has built a whole Grafana setup for it.
As long as you're immediately understanding, you'll continue to have fun.
As soon as you need to stop and research to understand what it is that you need to do next, and then start to re-architect a bunch of things, you may or may not realise this is the same thing you do for a living.
Nothing quite like burning the candle at both ends, for fun and profit.
Personally, it's not for me. Others (including some of my colleagues) definitely do not share that sentiment.
That said, I enjoyed Mindustry for a similar period too. Factorio-lite tower-defence game.
I took to TDD pretty easily because I was already used to doing short run-it-and-see-if-it-works cycles. The main difference was that instead of checking via eyeball, I talk the computer to check it. This is slightly slower early on, but so much faster once a program is big enough that manually checking everything would take a while.
We are advancing, but the current state is... not mind-blowing yet (albeit somewhat cool!). See [1] for an example interactive demo and [2] for the corresponding presentation.
Still a decade off from production, though. That it doesn't just take "test cases": you have to know how to formally express the program properties, which is a separate skill from both unit testing and implementing.
rev([1], [1])
rev([1, 2, 3], [3, 2, 1])
And maybe also that the following clauses are false: rev([1, 2], [1, 2])
rev([], [1])
rev([1, 1], [1])
Is Prolog able to infer, from the above, that rev([4, 5], [5, 4]) ? Or to synthesize the general form rev([H|T],R) :- rev(T, RT), concat(RT,[H],R) ?But if you look at logic programming with more expressive systems you can have something like what you propose. We describe what we expect to have and the system deduces a result. Not quite what you want but its closer.
Now there is also an ubiquitous logic system that many use: static typing. In a sense you are describing the general properties of something and the compiler infers optimizations based on your assertions. The concrete program is not a line by line translation from your code to machine code, but perhaps looked at in its entirety.
I agree that there is a lot of merit in pushing these things further and further. Right now we're kind of in a stage of patching things together. But I hope and assume that programming becomes more holistic in the future. Ironically we have to look at the past first, there was a lot of momentum in this direction up until the 80's roughly.
Aside from that, to produce a program that did what is actually required would require test cases and functions that cover the set of inputs and outputs. This is trivial for mathematical functions, but impractical (or impossible) for more general applications (e.g. anything dealing with human inputs).
This doesn't become practical until you can do it on a quantum computer with millions of cubits.
TDD is where you both write what you want, and you do the implementation also.
Show examples of French retirement pension computation, and watch if a computer can actually commit petit-suicide.
Beware that machine learning in a single pass/fail is more like having an infinite number of monkeys trying to write the works of Isaac Asimov.
[Edit/Update] All of the tests could be individual values, so non-zero (but nowhere near all ones) might help. Thanks for making me reconsider this, sdenton4.
Maybe you could move the goal/fitness function along the way. So start with something that compiles. Then having the desired input output. Etc.
Each 'bandit' is a random boolean outcome, governed by some hidden success probability. Thompson Sampling trades of exploration and exploitation. If there's no successes ever, then all bandits are equally bad, and you just keep exploring randomly until you find some success (or give up). If you do have some success, you can try to exploit it.
For a problem with continuous parameters, you can discretize the parameter space by binning, and then choose randomly within a bin for each trial. 'Exploiting' a particular bin might lead to breaking it into more bins for finer resolution.
https://gist.github.com/daveray/1441520
Even if you don’t follow along and try it, you can probably get the gist of how experimental / exploratory you can be.
I have (seriously!) never heard it reference like this at all. Every introductory article, every book, every tutorial, every video about TDD (or BDD) that I consumed, emphasised that it is about "workflow", about "a way of development" and not about percieved correctness.
Do note that the crucial different is that TDD > automated testing. In the sense that with TDD you write automated tests (that live in a test suite). But that "writing tests" or "developing a testsuite" is not nessecarily TDD.
In my personal opinion this claim goes too far. I think we can often explore aspects of design through code (including tests) but it can't be the main driver of design in any circumstance, nor does it by default or in general make our modules and programs more correct.
The key benefit of doing this is that we have to think of code in terms of invariants and testability. But that is just one of many, sometimes conflicting factors and there are certainly more approaches to doing this than writing automated (unit-) tests.
Right, in TDD there is "always" a range of values against which you are not testing. A developer can only create a finite amount of tests, and the program will fail with any of the missing permutations.
The TDD book itself is barely even about testing, it's almost entirely about development processes and different types of refactoring and how to use tests to get from a to b.
Correctness is rarely emphasized if at all.
> This is the beautiful thing about having a company that isn't on the stock market. Imagine you have a company that goes slower and slower every quarter, and then you confront the shareholders with the statement, that the way to solve it, is to do absolutely no new features for a quarter or two, refactor the code, learn new methodologies etc. I doubt that the shareholders would allow that. Luckily, we don't have any shareholders, and we understand the vital importance of this investment in the long run. Not only in the project, but also in our skill and knowledge, so we do better next time.
This is reassuring the notion of what I think actually matters, what the real essence is of developing a product, may that be a piece of art and entertainment (like here) or a productivity tool etc.
There are creators and there are consumers. We split them up by developers, designers, domain experts and so on, but what matters is that all the other participants, especially those who can exert power traditionally are not part of the essence and if not being careful and responsible, can easily add complexity and limitations that are entirely accidental and can even be harmful.
This reminds me of the agile manifesto, modern UX approaches and other processes that are driven by creators, but are often and very unfortunately being bent over backwards to fit into hierarchical power structures.
> TDD actually is the constant fast swithing between extending the tests and making them pass continously. So as you write tests, you write code to satisfy them basically at the same time. This allows you to instantly test what you write, and mainly use tests as specifiation of what the code should acctually do, which guides the thought process to make you think about where you are headed to, and to write code that is more structured and testable from the very beginning.
The important notion here is that TDD is not about tests and correctness, but about development. It continuously checks assumptions and explores the surrounding code, state and data until a sufficient solution is found.
If we squint a little we can see how closely related TDD with REPL Driven Development is. In essence it is the same thing and even has similar results, where the tests or REPL code can be left as an artifact for further, likely historical understanding.
We know now that neither is sufficient for a high degree of correctness, but they are certainly useful for understanding and development.