55

[Disclaimer: this question is subjective, but I would prefer getting answers backed by facts and/or reflexions]

I think everyone knows about the Robustness Principle, usually summed up by Postel's Law:

Be conservative in what you send; be liberal in what you accept.

I would agree that for the design of a widespread communication protocol this may make sense (with the goal of allowing easy extension), however I have always thought that its application to HTML / CSS was a total failure, each browser implementing its own silent tweak detection / behavior, making it near impossible to obtain a consistent rendering across multiple browsers.

I do notice though that there the RFC of the TCP protocol deems "Silent Failure" acceptable unless otherwise specified... which is an interesting behavior, to say the least.

There are other examples of the application of this principle throughout the software trade that regularly pop up because they have bitten developpers, from the top off my head:

  • Javascript semi-colon insertion
  • C (silent) builtin conversions (which would not be so bad if it did not truncated...)

and there are tools to help implement "smart" behavior:

However I find that this approach, while it may be helpful when dealing with non-technical users or to help users in the process of error recovery, has some drawbacks when applied to the design of library/classes interface:

  • it is somewhat subjective whether the algorithm guesses "right", and thus it may go against the Principle of Least Astonishment
  • it makes the implementation more difficult, thus more chances to introduce bugs (violation of YAGNI ?)
  • it makes the behavior more susceptible to change, as any modification of the "guess" routine may break old programs, nearly excluding refactoring possibilities... from the start!

And this is what led me to the following question:

When designing an interface (library, class, message), do you lean toward the robustness principle or not ?

I myself tend to be quite strict, using extensive input validation on my interfaces, and I was wondering if I was perhaps too strict.

3
  • I wonder what is the difference between HTML and general data? The robustness principle is about communication. One writes - one reads. Why network communication is different than visual or API? I have an API example where the principle of being liberal at what we accept simplifies the life of users who are programmers, reduces the code size and, therefore, improves performances + eliminates bugs. Look stackoverflow.com/questions/18576849
    – Val
    Commented Sep 2, 2013 at 16:37
  • @Val: actually, your example does not match. "being liberal in what you accept" is not just a matter of base/derived, it is going beyond that and also accepting (and interpreting) slightly erroneous inputs. Commented Sep 2, 2013 at 16:38
  • How showing some case does not show that case?
    – Val
    Commented Sep 2, 2013 at 16:40

13 Answers 13

42

I would say robustness when it doesn't introduce ambiguities.

For example: When parsing a comma separated list, whether or not there's a space before/after the comma doesn't change the semantic meaning.

When parsing a string guid it should accept any number of the common formats (with or without dashes, with or without surrounding curly braces).

Most programming languages are robust with white space usage. Specifically everywhere that it doesn't affect the meaning of code. Even in Python where whitespace is relevant, it's still flexible when you're inside of a list or dictionary declaration.

I definitely agree that if something can be interpreted multiple ways or if it's not 100% clear what was meant then too much robustness can end up being a pain though, but there's much room for robustness without being ambiguous.

8
  • 1
    I agree, robustness when it doesn't cost much is worthwile. Commented Oct 16, 2010 at 18:04
  • 2
    Even the comma separated list causes problems, kind of: chrome's and firefox's javascript engine seem to accept {"key": "value",} as valid, IE does not. I often stumbled upon this particular problem until i improved my build process with JSlint.
    – keppla
    Commented Jul 22, 2011 at 8:27
  • 2
    @keppla that's an issues with implementation rather than design. I'm not sure if that's legal by the javascript spec and IE isn't following, or if that's a "nice feature" that FF and Chrome added, but in Python it's specified to be valid and implemented as such. If it's specified as valid and it doesn't work, then it's a faulty implementation. If it's not specified then it shouldn't really be relied upon (although as a matter of practicality if it doesn't work in a major browser it might as well be considered not in the spec if you don't control the user)
    – David Ly
    Commented Jul 22, 2011 at 11:45
  • 7
    @Davy8: Trailing comma seem to be illegal (stackoverflow.com/questions/5139205/…). I dont want to rely on this, my point is, when enough people accept the input, it becomes de-facto standard (because one does not notice that it is an error), which leads to something we encountered in HTML: that the errors become so commonplace that you cannot ignore them as bad input anymore.
    – keppla
    Commented Jul 22, 2011 at 11:59
  • 2
    "When parsing a comma separated list, whether or not there's a space before/after the comma doesn't change the semantic meaning." - yes it does! If there's a space before a comma, that means there's a space at the end of the previous value! Silently trimming that space is the kind of problem this question is about. Commented Jan 5, 2016 at 4:19
19

Definitely not. Techniques such as defensive programming obscures bugs, making their appearance less likely and more random which makes their detection harder which makes isolating them more difficult.

The vastly under-rated Writing Solid Code was tremendous in repeatedly emphasizing the need, and techniques, of making bugs as difficult to introduce or hide. Through application of its principles such as, "Eliminate random behavior. Force bugs to be reproducible." and, "Always look for, and eliminate, flaws in your interfaces." developers will vastly improve the quality of their software by eliminating the ambiguity and uncontrolled side-effects that is responsible for a large quantity of bugs.

15

Overapplication of Robustness leads to you guessing what the user wanted, which is fine right up till you get it wrong. It also requires the completely misguided faith that your customers won't abuse your trust and create random gibberish which just happens to work, but that you won't be able to support in version 2.

Overapplication of Correctness leads to you denying your customers the right to make minor errors, which is fine right up until they complain that their stuff works fine on your competitor's product, and tell you what you can do with your 5,000 page standard that has the word "DRAFT" still scrawled on the cover in crayon, and at least 3 experts claim is fundamentally flawed, and 200 more honest experts say they don't fully understand.

My personal solution has always been deprecation. You support them, but tell them they're doing wrong, and (if possible) the easiest path to correctness. That way, when you turn the bug-feature off 10 years down the line, you at least have the paper trail to state that "we warned you this might happen."

1
  • 1
    +1 for deprecation, it is indeed an important concept and I am surprised that it was overlooked up until now. Commented Nov 15, 2011 at 12:11
11

Unfortunately the so called "robustness principle" doesn't lead to robustness. Take HTML as example. Much trouble, tears, waste of time and energy could have been avoided if browsers had strictly parsed HTML from the beginning instead of trying to guess the meaning of malformed content.

The browser should simply have displayed an error message instead of trying to fix it under the covers. That would have forced all the bunglers to fix their mess.

7
  • Quoting myself (must be getting old): "however I have always thought that its application to HTML / CSS was a total failure" Commented Nov 15, 2011 at 13:31
  • 9
    True, but the very same fault tolerance also helped to make web so popular.
    – MaR
    Commented Nov 15, 2011 at 16:58
  • 1
    The browser vendors failed on that one. With doctypes we had the ability to make our own choice in this very regard, but at the end of they day everything pretty much ended up behaving the same way as long as you had any doctype declared. Now they think a hyper-complex set of exacting rules to follow in regards to how to deal with failure is the solution? I think they're failing to identify the problem. Commented Apr 6, 2012 at 0:00
  • You are saying that fail-fast, which is opposite of "robust" is more efficient.
    – Val
    Commented Sep 2, 2013 at 16:36
  • @MaR: Is that so? Very disputable, far more likely the features were important. Commented Jan 5, 2016 at 6:33
8

I think that HTML and the World Wide Web have provided a wide-scale real-world test of the Robustness Principle and shown it to be a massive failure. It's directly responsible for the confusing mess of competing HTML almost-standards that makes life miserable for Web developers (and their users) and gets worse with every new Internet Explorer release.

We've known since the 1950s how to validate code properly. Run it through a strict parser and if something isn't syntactically correct, throw an error and abort. Do not pass go, do not collect $200, and for the love of all that is binary do not let some computer program attempt to read the coder's mind if he made a mistake!

HTML and JavaScript have shown us exactly what happens when those principles are ignored. Best course of action is to learn from their mistakes and not repeat them.

14
  • 4
    @ChaosPandion: the problem is not lying with Internet Explorer itself I think, but with all the non-standard web pages that were accepted by previous versions and that everyone now has to live with... and try to accomodate more or less successfully. Commented Oct 16, 2010 at 14:10
  • 5
    I actually think the IE team are in the worst possible position of all the browser developers. Joel sums up my thoughts pretty nicely. Commented Oct 16, 2010 at 23:22
  • 3
    It may have made life miserable for developers and designers, but then we'd be stuck with a very slowly evolving and static standard - I doubt the web would be like it is now given that. The real winners have been people browsing the web and at the end of the day they're the people who count.
    – FinnNk
    Commented Oct 30, 2010 at 21:19
  • 4
    Also, while the Robustness principle may make life hard for Web Developers, it's hard to call the World Wide Web (now an integral part of almost every major institution on the planet) a massive FAILURE.
    – deworde
    Commented Nov 15, 2011 at 10:47
  • 4
    "We've known since the 1950s how to validate code properly. Run it through a strict parser and if something isn't syntactically correct, throw an error and abort." To apply this to a real world scenario: If I've screwed up a single icon on the far right of my page just below the cut, giving up on the whole page is a really good way to send someone looking elsewhere, because of a problem they wouldn't even have noticed. Yes, you can argue I shouldn't have made the mistake. But that rather presupposes I haven't already gone to your more robust competitor and am no longer taking your calls.
    – deworde
    Commented Nov 15, 2011 at 10:51
8

I divide interfaces into several groups (add more if you like):

  1. those that are under your control should be strict (classes typically)
  2. library APIs, that should be also on strict side, but extra validation is advised
  3. public interfaces that must handle every kind of abuse that comes in (typically protocols, user inputs, etc). Here robustness on input really pays off, you can't expect everyone is going to fix their stuff. And remember for user it will be your fault if the application doesn't work, not the party who sent some ill-formatted crap.

Output must always be strict.

2
  • This is the right answer. Other people are forgetting that web browsers are chosen by and run on machines owned by people who simply want to read their web, therefore they have to act in the interest of the individual web reader as much as they can or the user will choose something else. That means making a best-effort attempt to read any web page.
    – bdsl
    Commented Aug 9, 2020 at 15:26
  • Would you employ a language translator to translate a documents for you to read if they refused to translate any document that had a typo in it? Or would you choose someone else?
    – bdsl
    Commented Aug 9, 2020 at 15:28
3

As a counterpoint to Mason's example, my experience with the Session Initiation Protocol was that while different stacks would interpret the relevant RFCs differently (and I suspect this happens with every standard ever written), being (moderately) liberal in what you accept means that you can actually make calls between two devices. Because these devices are usual physical things as opposed to pieces of software on a desktop, you simply have to be liberal in what you accept, or your phone can't call another phone of a particular make. That doesn't make your phone look good!

But if you're writing a library, you probably don't have the problem of multiple parties interpreting a common standard in mutually incompatible ways. In that case, I'd say be strict in what you accept, because it removes ambiguities.

The Jargon File also has a horror story on "guessing" a user's intent.

3
  • Very amusing story :) I realize that you might need more leeway when you try to interact with existing systems, since if it doesn't work you'll be blamed. Commented Oct 16, 2010 at 16:24
  • In fact, if your phone doesn't work with most other phones, your phone is bad.
    – SamB
    Commented Mar 28, 2011 at 21:33
  • 1
    @SamB: Replace bad with broken.
    – deworde
    Commented Nov 15, 2011 at 10:54
2

You're right, the rule applies to protocols, and not programming. If you make a typo while programming, you'll get an error as soon as you compile (or run, if you're one of those dynamic types). There's nothing to be gained by letting the computer guess for you. Unlike the common folk, we are engineers and capable of saying exactly what me mean. ;)

So, when designing an API, I would say don't follow the Robustness Principle. If the developer makes a mistake, they should find out about it right away. Of course, if your API uses data from an outside source, like a file, you should be lenient. The user of your library should find out about his/her own mistakes, but not anyone else's.

As an aside, I would guess that "silent failure" is allowed in the TCP protocol because otherwise, if people were throwing malformed packets at you, you would be bombarded with error messages. That's simple DoS protection right there.

1
  • 1
    "If you make a typo while programming, you'll get an error as soon as you compile " I present to you the millions of compiler WARNINGS a standard compiler will spit out, while still producing perfectly executable tasks.
    – deworde
    Commented Nov 15, 2011 at 11:11
2

It is true that the application of Postel's law in early browsers led to a terrible, terrible mess, but it has been argued (for example here: Trevor Jim - Postel’s Law is not for you) that the World Wide Web (and perhaps the entire Internet) could not have materialized, or it would not have been as successful as it was, if Postel's law had not been applied in those early days. (According to Wikipedia, Jon Postel wrote an early specification of TCP. The "law" summarizes his advice to the engineers who built the early Internet from scratch.)

Same thing with SIP as mentioned in another answer. The general consensus nowadays seems to be that Postel's law promotes interoperability in the short term but leads to complications in long term.

So, what we have here is a bit of a paradox: as disastrous as silent failure generally is, it might perhaps be beneficial when multiple competing parties are all working each on their own implementation of a certain technology without a complete and unambiguous standard in place for that technology.

However, that's a very rare situation to be in. The vast majority of software developers today are not working under such terrible conditions. Web browsers have switched from following Postel's law to refusing to render malformed content. (Albeit still without issuing a stop-the-world error message.) It is therefore inadvisable for the vast majority of developers today to be following Postel's law or engaging in any form of silent failure, bug hiding, or wannabe fault tolerance.

Fault tolerance isn't.

My two cents on how humanity could avoid getting into similar situations in the future: a committee producing a specification on paper is an amusingly traditional, dangerously low-tech, and woefully inadequate way of doing things. A specification for a technology is not worth the paper it is written on if it does not come together with a Reference Implementation.

The great thing about Reference Implementations is that they do not have to abide by the usual real world constraints of weight, dimensions, performance, attractive appearance, manufacturability, (let alone mass-manufacturability,) cost effectiveness, etc (heck, in many cases they do not even have to be physical!) so they are free to focus on enforcing the standard.

There is no piece of prose written on a specification that is as unambiguous as a corresponding piece of reference implementation that enforces it.

2
  • Nice answer. In addition, or in exchange, of a Reference Implementation, I have been think that a Reference Test Suite would also be a great way forward. And when two competing implementations both pass the test suite yet implement a different behavior, then the hole in the suite is fixed. Commented Aug 9, 2020 at 17:46
  • @MatthieuM. yes, that's absolutely right. I should probably amend the answer to include this.
    – Mike Nakis
    Commented Aug 9, 2020 at 18:39
2

The robustness principle only applies when implementing an ambiguous specification.

For example, imagine a protocol specification which states:

A request payload starts with the word GET in uppercase or lowercase.

This would be ambiguous - GET and get is clearly both allowed but it is not absolutely clear if e.g. Get or gEt is allowed. So following Postels Law, you accept all case combinations in input, but you make sure to only emit either GET or get. This ensures the highest chance of interoperability if other implementors have interpreted the ambiguous parts differently.

But ideally, a protocol specification should be completely unambiguous. The real HTTP spec states for example:

By convention, standardized methods are defined in all-uppercase
US-ASCII letters. [...] When a request method is received that is unrecognized or not implemented by an origin server, the origin server SHOULD respond with the 501 (Not Implemented) status code.

So this makes it clear that only GET is recognized and Get or get should result in an error code. An implementation that accepted Get would not be "robust" or "liberal", it would be non-compliant and broken.

Silently ignoring invalid input is fine if the spec explicitly allows it. For example, TCP is allowed to silently drop packages because the protocol is designed to handle unreliable connections anyway. But silently ignoring a malformed HTTP request is not allowed by the HTTP spec.

The Robustness Principle is sometimes confused with flexibility in acceptable input - for example JavaScript having optional semicolons. But JavaScript semicolon insertion is specified in detail in the ECMAScript standard. Whether it is good language design is a separate question, but there is no ambiguity in the specification and therefore no need to apply the robustness principle in the implementation.

Unspecified behavior in C should not be confused with Postels Law either. The C spec is not ambiguous, rather it gives very specific parameters for what freedom an implementation has.

HTML and CSS are notorious for differences and incompatibilities across implementations. But Postels Law is not to blame for this. The problems are due to:

  1. Bugs, mistakes and omissions in implementations.
  2. Ambiguities or lack of detail in the standards

For example, the infamous "quirks mode" was due to bugs in the browser rendering engine. Most significantly IE mistakenly included padding and border in the calculation of width in the box model. Arguably, the spec was ambiguous, but the choice did not have anything to do with Postels Law since neither formula is more "liberal" or "strict" than the other.

Having a detailed spec which covers all eventualities is always the most robust solution. If you are designing a protocol, you should take care to avoid ambiguities in the specification.

When you are designing a library or API you decide on the interface yourself, so the principle is not relevant. It is only relevant when integrating with component you have no control over.

But in the very specific case where you are implementing a protocol with ambiguities in the specification, I will argue the robustness principle is common sense. Just don't apply the principle outside of this context.

1
  • Robustness principle applies to specifications in the first place. Specification should only allow required minimum. Be extensible, not permissive.
    – Basilevs
    Commented Jul 14 at 13:47
1

IMO, robustness is one side of a design trade-off not a "prefer" principle. As many have pointed out, nothing stinks like blowing four hours trying to figure out where your JS went wrong only to discover the real problem was only one browser did the proper thing with XHTML Strict. It let the page go to pieces when some portion of the served HTML was a complete disaster.

On the other hand, who wants to look up documentation for a method that takes 20 arguments and insists they be in the exact same order with empty or null value place holders for the ones you want to skip? The equally awful robust way to deal with that method would be to check every arg and try to guess which one was for what based on relative positions and types and then fail silently or try to "make do" with meaningless args.

Or you can bake flexibility into the process by passing an object literal/dictionary/key-value pair list and handle the existence of each arg as you get to it. For the very minor perf tradeoff, that's a cake and eat it too scenario.

Overloading args in intelligent and interface-consistent ways is a smart way to be robust about things. So is baking redundancy into a system where it's assumed packet delivery will regularly fail to be delivered in a massively complicated network owned and run by everybody in an emerging field of technology with a wide variety of potential means for transmission.

Tolerating abject failure, however, especially within a system you control, is never a good tradeoff. For instance, I had to take a breather to avoid throwing a hissy fit in another question about putting JS at the top or bottom of the page. Several people insisted that it was better to put JS at the top because then if the page failed to load completely, you would still potentially have some functionality. Half-working pages are worse than complete busts. At best, they result in more visitors to your site rightly assuming you're incompetent before you find out about it than if the busted up page is simply bounced to an error page upon failing it's own validation check followed by an automated e-mail to somebody who can do something about it. Would you feel comfortable handing your credit card info over to a site that was half-busted all the time?

Attempting to deliver 2010 functionality on a 1999 browser when you could just deliver a lower tech page is another example of a foolhardy design tradeoff. The opportunities blown and money I've seen wasted on developer time spent on bug-ridden workarounds just to get rounded corners on an element hovering above a !@#$ing gradient background for instance, have completely blown me away. And for what? To deliver higher tech pages that perform poorly to proven technophobes while limiting you choices on higher end browsers.

In order for it to be the right choice, the choice to handle input in a robust manner should always make life easier on both sides of the problem, in the short and the long term IMO.

1
  • 4
    "for a method that takes 20 arguments" > no need to look further, after 5/6 the method is wrong. Thanks for the answer though :) Commented Apr 6, 2012 at 6:04
1

Never fail silently. Apart from that, trying to guess on what the user of an API/library wanted, does not sound like a bad idea. I would not follow it though; having a strict requirement, can expose bugs in the calling code and/or misinterpretations about your API/library.

Furthermore, as has already been pointed out, it depends on how hard it is to actually guess what the user expected. If it's very easy, then you have two cases:

  1. Your library should be designed a bit differently (rename some function or split it in two), so that the user can expect what you actually provide.
  2. If you believe that your library is designed properly, meaning clear / straightforward naming, then you can try to infer what the user intended.

In any case that it is not 100% obvious and deterministic, that one input should be converted to another, you should not do the conversions, for a number of already mentioned reasons (breaking compatibility on refactoring, least astonishment of users).

When dealing with an end user, trying to fix their input/guess is very welcome. He is expected to enter invalid information; this case is completely unexceptional. Another developer though, is not a simple non technical user. He has the expertise to understand an error, and the error can have significance/be beneficial to him. Thus, I agree with you on designing strict APIs, while -of course- strictness is accompanied by clarity and simplicity.

I would recommend that you read this question of mine, of a similar case.

-1

Here's an addition to previous answers as I came across an issue that can put the principle of robsustness in more concrete perspective.

Be conservative in what you send; be liberal in what you accept.

What's important to understand is that the data an application/method send is supposed to be in a valid state and conformant to a certain standard. For that reason, an endpoint shouldn't have to check that the data is really conforming to the standard.

What it also means is that because reality quite differ from expectations, if you do receive invalid data, the thing that should be asked is if it really matters. If you can implement a superset of a standard it means you can accept more data than the actual strict standard can handle.

One example is a CSV file... By default a comma separated value is simple, it's just anything separated by , without talking about text containing ,. One strict parser could parse a value based on a regex like this:

VALUE = is_number | is_alpha_numeric | is_ponctuation | ..
SEPARATOR = ","
CRLF = "\r\n"
LINE = VALUE (SEPARATOR VALUE)* CRLF

But in reality, you don't have to check if the value contains text... As long as the value doesn't contain a SEPARATOR it can be considered as a value.

So if you want to parse a line, you can easily parse CSV files in ascii,utf-8 encodings without issue as "," will not overlap on value chars.

Your parser may end up like this:

VALUE = !SEPARATOR | ESCAPE SEPARATOR
ESCAPE = "\\"
SEPARATOR = ","
CRLF = "\r\n"
LINE = VALUE (SEPARATOR VALUE)* CRLF

The code will end up faster and will easily load many type of csv file regardless of the content in them.

In HTML the same thing happen with HTML elements, they are self closed because the moment you open them in the memory you have an element. You really don't need to close it. When you reach a close element you can try to pop the current element until you reach an element you can close so

<p>
   <div>
       <div>
           Text
           <span>Text
<p>
   <div>
      Text2

Will give the resulting tree:

<p>
   <div>
       <div>
           Text
           <span>Text</span>
       </div>
   </div>
<p>
   <div>
      Text2
   </div>
</p>

It's not because HTML parser were doing tricks but because it was easier to check that when a P tag is opened that you should create that new P tag as a sibling of a P tag already open. The close tag are totally optional because the standard really just prevent having nested P tags.

When the parser match a closing tag it will just walk up the parent element and close all the tags until it finds a match... If you have a closing tag for a tag that wasn't open, it could be up to the browser to ignore it or create a parsing error. But the point is mainly that the application shouldn't crash and be able to handle more than the standard explicitely tell because reality is you shouldn't see an application crash because something went wrong or being completely unusable because people using the system aren't perfect and sometimes makes mistakes.

That's why there are types of error that are unrecoverable and somes that are and you shouldn't spend time validating that an input conforms to a specific standard as long as you can parse it.

One example is how a parser could completely ignore the encoding inside the < and > of an html attribute so it would be possible to have html attributes in other languages on ASCII.

In a case I had recently, which is a bit like the case for the CSV parser, checking for valid data actually decreased the performance by 10 times when in reality I only had to check for some separators and then if the value have invalid data that prevent something to work it could be handled somewhere else... But if want to check a match of bytes against other bytes, you don't really care about the encoding in them...

I prefere to have a 10times boost in speed and accept invalid header names in an http header than having request handling 10 times slower but fail as soon as something isn't as expected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.