Hacker News new | ask | show | jobs
by mempko 666 days ago
You would obviously test the software before it goes to the users. With contracts you would be able to have information from your users that make it MUCH easier to fix. Without contracts you will have a much harder time fixing the bugs. Contracts catch bugs unit tests don't.
1 comments

> You would obviously test the software before it goes to the users

That... is literally what unit tests do. Test the software before you give it to users.

You seem confused what the debate is over. Nobody is arguing against contracts. They're awesome. They're just not substitutes for unit tests (or vice versa for that matter). You guys have been arguing against unit tests, which is an absurd position to take, hence this whole debate.

It's not absurd. Unit test are expensive and slow you down while at the same time not targeting the part of software where bugs occur. It's usually HOW functions are composed that cause bugs, not the functions themselves. Contracts rest the interaction of components while unit tests don't.
To be fair, "functions themselves" seldom form a valid unit, so in practice you'd end up having to test the composition of said functions.
This is definitely absurd... but okay, riddle me this then. Say you're reimplementing string.indexOf(string) with your fancy new algorithm so you can deliver a nice speed boost to users. How do you do your best to minimize the chances of it blowing up on everyone without writing unit tests?
First, users don't use string.indexOf(string). Users use software that uses string.indexOf(string). Second, I would write a test that takes generates a variety of inputs to cover the domain of string.indexOf(string) and have it call the old version and the new version. I would then collect stats around the performance of each call and make sure the new implementation is faster by the threshold I meant for it to be. Since the PURPOSE OF THE REFACTOR WAS PERFORMANCE. Then I would delete the test after I'm satisfied it worked well on the target hardware, etc.

Let's look at the chrome unit tests.

https://chromium.googlesource.com/v8/v8/+/3.0.12.1/test/mjsu...

This test checks one case and then does a loop over to make sure various sizes work. This is a POOR test because it doesn't cover the domain of the function and exceptional cases. This test is pointless to have and run every build. Why keep it, it does nothing useful.

My point is that if you are refactoring a function, you are going to have to write a NEW TEST anyway because the purpose of a refactor is different each time. The old tests are going to be useless. This is why contracts are so useful because they will ALERT YOU when the software does something you didn't assume. Unit tests don't do this.

From an information theory perspective, the old tests would not provide you with new information. If you want to make sure your refactor works like the old function, then compare the outputs of your new function with the old one over the domain of the function. Then if it's good, delete the tests because why keep them.

Experience shows that users will do things to your program you did NOT expect and therefore your tests will not cover. And more likely than not, refactoring requires rewriting tests anyway so the old tests are usually always useless.

Now SYSTEM tests ARE useful. Tests that wire up many components and test them are useful. But a UNIT test, which tests one function is often just a waste of time to keep.

Honestly... I can see it's going to be genuinely exhausting for me to rebut point-by-point here, so I'll just let others read this and continue if they're interested. I really appreciate the response though, at least it explains your thought process well.

I have one question for you that I feel I have to ask: have you actually practiced commercial software development on a team (say, 5+ developers on the same codebase) in this manner that you describe for a significant period of time (say, 2+ years)? and if so, do you feel the resulting software has been robust and successful?

I've been doing this style of software development for 16 years (out of 28 years programming) across various projects.

If you want a small project that I wrote to look at, see henhouse https://github.com/mempko/henhouse. I wrote an article talking about design by contract and why it's better than TDD here https://mempko.wordpress.com/2016/08/23/the-fox-in-the-henho...

I've built a computer vision system that processed petabytes of data with only one or two bugs in production for any given year. At any given time we kept a bug list of zero. For the last five years I built a trading system using this same process. Again, we don't keep a bug list because if there were bugs, the system wouldn't even run. And if there is a bug we have to fix it immediately. We do have tests, but they are system tests. The worst bug we had that took too long to catch was in a part of the system that DIDN'T use contracts.

Design by Contract is a secret weapon people don't seem to know about.

Also see https://github.com/mempko/firestr, which I used Design by Contract extensively. It's a GUI program so the user can do crazy things.

Learn Design by Contract. Do system testing. Do exploratory testing. Do fuzz testing. Keep a bug list of zero. Don't waste time on unit testing.

If you are looking for popular software built using Design by Contract to various degree.

See SQLite (uses assertions heavily), .NET framework from Microsoft, Many of the C++ Boost libraries, Parts of Rust, Parts of Qt. The Vala programming language, Ada GNAT... and many others.

Here is a research paper from Microsoft that shows the advantage of contracts and code quality. https://www.microsoft.com/en-us/research/wp-content/uploads/...

Replying here to your last post here because HN won't let me reply to it (maybe too nested?). You say nobody is arguing against contracts.

> So you'd prefer your contracts to blow up your bugs in your clients' faces, rather than catch bugs yourself prior to releasing the code to them?!

That's you arguing against contracts. Contracts need to blow up when users use the software (including you and your testers before you ship). You should ship if you find no contracts blowing up. But you need to let them blow up in user faces too. They provide valuable information AFTER SHIPPING. Otherwise they lose a lot of their value.

Saying contracts shouldn't run in shipped code misses the whole point about what contracts are.

> That... is literally what unit tests do. Test the software before you give it to users.

No, unit tests test a portion of the software before shipping. My argument is they aren't worth the cost and provide little value. Most of the value comes from SYSTEM tests, integration tests, exploratory testing, fuzz testing, etc. Unit tests are the weakest form of testing.

Here is a great argument against them called Why Most Unit Testing is Waste by Coplien. It's a dense argument which I agree with.

https://wikileaks.org/ciav7p1/cms/files/Why-Most-Unit-Testin...

I'm not mempko, but I can answer that question---yes. For over a decade on the same code base with a team that ranged from 3 to 7 over the years. We did have what would be considered end-to-end tests, or maybe integration tests that tested the entire system from ingress (requests coming in) to egress (results going out) that ensured the existing "business logic" didn't break.

There were no unit tests, as a) the code wasn't written in a style to be "unit" tested, and b) what the @#$@Q#$ is a unit anyway? Individual functions in the code base (a mixture of C89, C99, C++98 and C++03) more or less enforced "design by contract" by using calls to assert() to assert various conditions in the code base. That caught bugs as it prevented the wrong use of the code when modifying it.

Things only got worse when new management (we were bought out) came in, and started enforcing tests to the point where I swear upper management believed that tests were more important than the product itself. Oh, the bug count shot up, deployments got worse, and we went from "favorite vendor" to "WTF is up with that vendor?" within a year.