Obey Postel’s Law in Your Code

Jon Postel was a guy that wrote an early specification for the Transmission Control Protocol (TCP), one of the core protocols of the Internet protocol suite.  You use it every day to surf the web, send emails, etc.  One of the guiding principles that he used when writing it was this:

Be conservative in what you do, be liberal in what you accept from others.

This idea is also call the Robustness Principle, and is sometimes rephrased as “Be conservative in what you send, be liberal in what you accept.”  When applied to TCP, it means that the sender of data should be strict in what is sent, ensuring that it is accurate and precise.  It also means that the receiver of data should be forgiving and understanding of data to as large a degree as possible.  If you send data, be as clear as possible in what you send.  If you can accept the data sent, then you should.

Any application programming interface (API) should follow this principle.  Thus, the same principle should apply to your code.  The public interface of your class should be viewed as an API, and it should be conservative in what it sends, and forgiving in what it receives.  When calling another API, your code should send out data in a completely conformant way, following the rules laid down by the receiving API.  But it should be willing to receive input in a non-conformant way as long as the input can be understood.

For instance, if passed a string, you might be happy to accept strings with blank spaces on the beginning and end, and use the Trim function to clean things up for the sender.  Your classes might provide overloads for input methods, accepting both and integer and a string as input, providing a way for your class to be as forgiving as possible.

But when you call another API, your code should be strict and always send data in the correct, expected form.  You should trim your strings before they get sent along to the API you are calling.  If the system expects integers, you are going to send integers.  You should meet the specification completely.

Another example is the use of nil.  First, your code should never pass nil to a method that you are calling.  Always provide a valid instance if the API calls for one.  Second, your code should accept nil, but “fail fast” if passed nil and raise an exception.  You should never let your internals get into the state of nil, and while you should accept nil, you should immediately raise an exception at any attempt to set one of your internal references to nil.

Postel’s Law – a small but important way to write better code.

6 Replies to “Obey Postel’s Law in Your Code”

  1. Mason — Maybe you missed my point or maybe I articulated it poorly. I wasn’t arguing that you should guess what the input is or even that the TCP/IP protocol is designed with the right precursors. I was pointing out how *code* should be written, and I will argue that your code should be as forgiving as possible about what it accepts as input, or that it should fail fast if the input doesn’t make sense.

    I agree that input should be validated. In fact, I believe I say that very thing. But that doesn’t mean it is wrong to accept input in multiple forms, for instance.

    Main Point: If input is validate-able, then it should be accepted. What’s wrong with that?

    • What’s wrong with it is that in your examples, you go beyond validating input: you change it until it meets your idea of validity. For example, you specifically mentioned automatically trimming strings. What happens, then, if someone wants to deliberately provide indented or padded text? Depending on the nature of the input, there are plenty of valid use cases for that.

      How many times have you been typing something in Word and it automatically makes some “correction” for you and that wasn’t what you wanted? Or you try to highlight part of a word with the mouse, and it goes and “helpfully” highlights the whole thing for you? Or you try to drag a window around in Windows 7, and you get too near one of the edges of the screen and it expands the whole window and starts sticking to the edge when all you were trying to do was reposition it? Doesn’t that drive you up the wall?

      That’s the Robustness Principle in action: try to be “helpful” by doing something other than what the input literally said to do. And it’s a pain in the butt 9 times out of 10.

      • Again, I guess I’m not being clear. You’d trim the strings if your input required trimmed strings. If non-trimmed strings were acceptable, then you wouldn’t trim strings. In other words, my class is the arbiter of what is valid — so yeah, I’m going to change it to meet my idea of validity if it can be so changed.

        I’m not arguing to accept *anything*, I’m arguing to accept anything that is able to be validated according to the rules.

        Again, I see nothing wrong with that.

  2. The first piece of code is what most programmers do:

    
    function divide(a,b:double):double;
    begin
      if IsZero(b) then
        raise EOutOfRangeException.Create('B must be non-zero');
    
      Result := a/b;
    end;
    
    begin
      a := 10; b := 0;
      try
        WriteLn( divide(a,b) );
      except
        WriteLn('whoops :( something bad happened');
      end;
    end.
    

    The function needs to escalate because it runs into a situation that it cannot handle. The caller needs to handle this situation, and when it happens, it cannot know why an exception was called, or what state the software in now.

    At university, we were taught to take a very mathematical approach to programming, and to think in pre- and post conditions. The above code is not the best way of writing testable and predictable code. It’s difficult to prove the correctness of code that relies on exception handling.

    What my university professor suggested us to do instead is to always validate pre-conditions outside of a function, like this:

    
    
    // precondition: b != 0
    // postcondition: result is a divided by b
    function divide(a,b:double):double;
    begin
      // NO validation should take place here.
      // For convenient debugging you could add asserts here, but we were discouraged to do this:
      // assert(not IsZero(b));
    
    
      Result := a/b;
    end;
    
    begin
      a := 10; b := 0;
      if Iszero(b) then
        WriteLn('b must be non-zero')
      else
        WriteLn( divide(a,b) )
    end.
    

    What the article suggests is to get rid of as many pre-conditions as possible, which makes sense.
    Using the same examples, it would mean writing code like this:

    
    // no preconditions
    // postcondition: you get 0, unless b is non-zero. In that case the result is a divided by b
    function divide(a,b:double):double;
    begin
      if Iszero(b) then
        Result := 0
      else
        Result := a/b;
    end;
    
    begin
      a := 10; b := 0;
      WriteLn( divide(a,b) )
    end.
    
  3. I tend to think that being conservative with what you accept is generally better – but provide really nice error messages, sure be friendly, but if you have a specification then you should stick to it.

  4. Yes, I use the robustness principle of being strict on output, but is flexible for the input. I did not know that it had a specific name, it is just something I have done the last 25+ years. It is especially important where you have input that people type in. E.g. for a payment form you might have to enter an IBAN, this bank account code is officially written with groups of 4 chars and a space between each group, letters are upper case. It would be a very annoying program if it does not accept the user to type it as one long number and use lower case letters. The program should just accept the user input, validate it and reprint the input with official formatting. This immediately shows the user what input is going to be used.

Comments are closed.