1. 8

  2. 12

    This one always irritates me every time someone posts it.

    That’s what the “contract” is about. It’s a two way binding.

    I promise to deliver what I said in the contract, you promise to use only what is in the contract.

    I choose to reimplement my side of the contract, but hold to the contract, and your side breaks, it’s your problem.

    That’s what contracts are for.

    To work out who needs to clean up their shit.

    You may say, ouch, it’s too much work for me please give me back the old behaviour… but that’s a New contract (and a New Price).

    More common is the contract was too informally specified and ambiguity arose in the interpretation.

    Or even worse Connascent Coupling arose because the two sides “Grew Up Together”.

    Personally I regard Connascent Coupling as the enemy of good software and kill it whenever I see it. (Which is often).

    All very good reasons never to use closed source opaque binary blobs.

    1. 4

      You may say, ouch, it’s too much work for me please give me back the old behaviour… but that’s a New contract (and a New Price).

      What if you depend on that customer? They can switch to another provider at some inconvenience to themselves, but then you might go bankrupt. Do you hold your ground?

      What if it’s a third party that’s harmed by the new behavior? B relies on your “implicit interface” and C uses B. When your change breaks B, C thinks it’s your fault. After all, B didn’t change their software, you changed yours.

      1. 2
        1. If you have one enormous customer then they can make a lot of demands on you regardless. This would be just one example of them monopolizing your services. You are basically a contractor for them.

        2. Misuse that will void the product warranty are, but not limited to … etc.

        1. 2

          Misuse that will void the product warranty are, but not limited to … etc.

          I’m trying to find an article I read where SimCity (or doom, one of those two) relied on undocumented behavior of the DOS memory manager. When windows 95 changed that it broke the game, which customers blamed on MS and not the game company.

          1. 3

            It was SimCity. (Search for “SimCity”, since Joel doesn’t have internal anchors in that article; and I can’t find a primary source for that story.)

        2. 2

          What if you depend on that customer?

          Still new contract, new price, but your management may choose to pay the price for the customer. ie. On the basis that Good Will buys more custom than ads, ie. it’s essentially coming out of the advertizing budget, but at that level accounting rules are more politics than arithmetic.

          The contract declares B is to blame, however, how and by who it is fixed can be a matter of a new negotiation and a new contract.

          If I’m writing the code on both sides of the contract, I choose the solution that minimizes the total amount of work.

        3. 1

          If we take “interface” to mean “contract,” where Hyrum’s Law and your perspective diverge?

        4. 2

          One possible strategy is design it to fail during testing on anything not in the contract. I mean, obviously want to be cautious about doing this at all or how one does it. I just remember some people using it. Netflix’s Simian Army or other fault-injection of distributed systems are examples where reliable, ordered messaging might be assumed in code despite no contract or implementation for it.

          1. 4

            It’s hard to test for stuff that’s not in the contract but is in the code. Let me tell you a story of something that happened to me yesterday.

            I have a script that updates Mu on my students’ servers. However, my students sometimes modify Mu themselves (they are encouraged to do so). So the way my script works is that it runs rsync -n (dry run), shows me what would happen, then asks me for confirmation that all is well before performing a real rsync. If I see changes from my students, I cancel the operation and perform a more manual merge.

            This workflow has been pretty much unchanged for a couple of years. The only change was that I switched the script to use /bin/sh a year or so ago, as part of a recent kick to minimize dependencies. And everything continued to work fine. A month ago I upgraded Ubuntu on my machine to 16.04, and yesterday (teaching has been slow recently because life) I ran my script for the first time after the upgrade – and it ran the rsync without prompting me at all. A little digging showed that my approach of waiting for a prompt by running just read was a violation of Posix, and on Ubuntu Xenial /bin/sh now hews closer to the letter of Posix, raising this error:

            read: arg count

            read needs to pass a variable to save the input in, or read _ to not save the input.

            I’m not sure what the lesson is here. My bias is to think contracts are shit, because people don’t read contracts. But maybe this is a learning experience to change my mind.

            1. 1

              Remember that contracts a la DbC in a language with good tooling can be runtime checks or generate tests to ensure they’re being used. They can ignore the contracts but the check you leave in won’t ignore the bad input.

              Some things, esp in build systems, need human review to catch, though. What you described seems to be a side effect of UNIX style of composing programs without contracts in a mix of unsafe and informal languages. The problems that came from such stuff are why stuff like contracts were invented and deployed.

              1. 1

                Oh I see, by “contract” you mean “formal contract”. I think the point of OP is that it’s impossible to enumerate the “intended contract” in all particulars as a “formal contract”. Because if you could, you’d just make those scenarios well-defined.

                More rigorous and formal languages will make these corner cases rarer, but they don’t actually obsolete OP. They just push up the “sufficient number of users”.

                1. 1

                  Hmm, on reflection my comment is bad. OP conflates Hyrum’s law with XKCD 1172 and I was mindlessly following along, but really they’re separate scenarios. The XKCD is about unintended uses of a piece of code. Hyrum’s law is about intended divergence between code and some spec. Between the two Hyrum’s law is actually easier. If you have a spec it is possible in principle to catch violations as you surmise. There’s just the question of what the costs are. The XKCD is however about violations you didn’t even know you cared about. Hyrum’s law is about known unknowns. XKCD 1172 is about unknown unknowns.

                  1. 1

                    “Oh I see, by “contract” you mean “formal contract”.”

                    I mean both depending on what we’re talking about. It was originally API but can also be formal contracts. The thing is that unspecified or poorly-specified things won’t be checked by default. So, you gotta mandate they get checked, make them fail in ways that nobody relies on them versus correct behavior, or deal with them plus what people are doing yourself. These are a few possibilities that come to mind.

                    1. 1

                      I wrote a DNS packet decoder library and to ensure safety, I check every bit of the incoming packet. There’s one bit left undefined (no RFC defines it as far as I know), and if it’s not 0, I reject the packet. Am I too intolerant of the DNS packet contract?

                      1. 1

                        I can’t give you a right answer to how to handle stuff in Internet protocols given what users will demand and implementations will do is so out of most of us’ control. Internet standards are a much bigger problem than an API for your personal project or commercial product. I will tell you some things that came to mind reading the question:

                        1. The stance I have on API’s is pro formal specification. That’s because just formally specifying things has caught problems. Comparing output of random implementations with executable, formal specifications also caught problems. In your article, you report that your code that follows the spec… that leans toward an executable specification… caught problems in other implementations. That’s a valuable thing that matches the prediction.

                        2. There’s a middle ground that says you can accept something but log it to analyze situation further. What you find might lead to changes in your spec if not the true one. Also, maybe run it with more checks on or isolation as your protocol engine goes through it. In micro and separation kernel schemes, there’s often simple functionality that’s trusted due to its well-vetted spec/implementation. Everything else got re-routed through user-mode components whose output is validated for sanity. Any explosions the unusual features cause will be contained.

                        3. Postel’s law. If app need to integrate well with 3rd parties’ apps, then you might need to be able to accept any crap the products around you do just to succeed financially or with uptake if FOSS. Alternatively, your solution might be replacing another that the users built a lot of code on that expects the out-of-spec behavior of the original a bit. For FOSS, the struggles of OpenOffice to make headway in a Microsoft world designed for lock-in to existing doc files are a perfect example. On protocol side, another is comparing behavior of your protocol implementation against many others to either create a superset spec containing all of it or a series of profiles the user can select based on what their internal environment expects. People without legacy baggage get your most robust version with others taking on as much risk as they already chose to.

                        So, those are the three things that come to mind reading your question followed by your article. On DNS side, I think people commonly try to copy whatever behavior is accepted by most popular clients with least trouble with middleboxes. I have no idea what that set is, though. I’d imagine it changes over time, too, where you’d have to constantly run tests probably with help of other vendors or their customers, too.