Comment by conartist6

9 hours ago

Remember that these "laws" contain so many internal contradictions that when they're all listed out like this, you can just pick one that justifies what you want to justify. The hard part is knowing which law break when, and why

Postel's Law vs. Hyrum's Law is the canonical example. Postel says be liberal in what you accept — but Hyrum's Law says every observable behavior of your API will eventually be depended on by someone. So if you're lenient about accepting malformed input and silently correcting it, you create a user base that depends on that lenient behavior. Tightening it later is a breaking change even if it was never documented. Being liberal is how you get the Hyrum surface area.

The resolution I've landed on: be strict in what you accept at boundaries you control (internal APIs, config parsing) and liberal only at external boundaries where you can't enforce client upgrades. But that heuristic requires knowing which category you're in, which is often the hard part.

  • I’m one of those that have thrown out Postel’s law entirely. Maybe the issue is that it never defines “strict”, “liberal”, and “accept”. But at least for public APIs, it never made sense to me.

    If I accidentally accept bad input and later want to fix that, I could break long-time API users and cause a lot of human suffering. If my input parsing is too strict, someone who wants more liberal parsing will complain, and I can choose to add it before that interaction becomes load-bearing (or update my docs and convince them they are wrong).

    The stark asymmetry says it all.

    Of course, old clients that can’t be upgraded have veto power over any changes that could break them. But that’s just backwards compatibility, not Postel’s Law.

    Source: I’m on a team that maintains a public API used by thousands of people for nearly 10 years. Small potatoes in internet land but big enough that if you cause your users pain, you feel it.

    • One example where I think the law does make sense is for website URL paths.

      Over time the paths may change, and this can break existing links. IMO websites should continue to accept old paths and redirect to the new equivalents. Eventually the redirects can be removed when their usage drops low enough.

    • I probably use a different interpretation of Postel's law. I try not "break" for anything I might receive, where break means "crash, silently corrupt data, so on". But that just means that I return an error to the sender usually. Is this what Postel meant? I have no idea.

      2 replies →

  • I used to see far more references to Postel’s law in the 00s and early 10s. In the last decade, that has noticeably shifted towards hyrum’s law. I think it’s a shift in zeitgeist.

  • I've always thought of Hyrum's Law more as a Murphy-style warning than as actionable advice.

    • Yea hyrum's law is like an observation of what happens when you personally try to make 200,000+ distinct commits to google3 to fix things that are broken (as hyrum did, mind you this is before LLMs), and people come climbing out of the woodwork to yell at you with fistfuls of xkcd/1172

  • I look at Postel’s law more as advice on how to parse input. At some point you’re going to have to upgrade a client or a server to add a new field. If you’ve been strict, then you’ve created a big coordination problem, because the new field is a breaking change. But if you’re liberal, then your systems ignore components of the input that they don’t recognize. And that lets you avoid a fully coordinated update.

  • This reminds me of a comment I read here a long time ago; it was about XML and how DTDs were supposed to permit one to be strict. However, in reality, the person said, if the the other end who is sending you broken XML is a big corp who refuses to fix it, then you have no choice but accept it.

    Bottom line: it's all a matter of balance of powers. If you're the smaller guy in the equation, you'll be "Postel'ed" anyway.

    Yet Postel's law is still in the "the road to hell is paved with good intentions" category, for the reason you explain very well (AKA XKCD #1172 "Workflow"). Wikipedia even lists a couple of major critics about it [1].

    [1] https://en.wikipedia.org/wiki/Robustness_principle

    • Would be tempted to stick a proxy in there that checks if the data is malformed in the expected way, and if so converts it to the valid form before forwarding to the real service.

DRY is my pet example of this.

I've seen CompSci guys especially (I'm EEE background, we have our own problems but this ain't one of them) launch conceptual complexity into the stratosphere just so that they could avoid writing two separate functions that do similar things.

  • I think I remember a Carmack tweet where he mentioned in most cases he only considers it once he reaches three duplicates

    • The "Rule of 3" is a pretty well known rule of thumb; I suspect Carmack would admit it predates him by a fair bit.

    • I once heard of a counter-principle called WET: Write Everything Twice.

    • Why 3? What is this baseball?

      Take the 5 Rings approach.

      The purpose of the blade is to cut down your opponent.

      The purpose of software is to provide value to the customer.

      It's the only thing that matters.

      You can also philosophize why people with blades needed to cut down their opponents along with why we have to provide value to the customer but thats beyond the scope of this comment

      10 replies →

  • I worked for a company that also had hardware engineers writing RTL. Our software architect spent years helping that team reuse/automate/modularize their code. At a mininum, it's still just text files with syntax, despite rather different semantics.

  • I've heard that story a few times (ironically enough) but can't say I've seen a good example. When was over-architecture motivated by an attempt to reduce duplication? Why was it effective in that goal, let alone necessary?

    • I think there is often tension between DRY and "thing should do only one thing." E.g., I've found myself guilty of DRYing up a function, but the use is slightly different in a couple places, so... I know, I'll just add a flag/additional function argument. And you keep doing that and soon you have a messed up function with lots of conditional logic.

      The key is to avoid the temptation to DRY when things are only slightly different and find a balance between reuse and "one function/class should only do one thing."

      1 reply →

    • Buy me a beer and I can tell you some very poignant stories. The best ones are where there is a legitimate abstraction that could be great, assuming A) everyone who had to interact with the abstraction had the expertise to use it, B) the details of the product requirements conformed to the high level technical vision, now and forever, and C) migrating from the current state to the new system could be done in a bounded amount of time.

      My view is over-engineering comes from the innate desire of engineers to understand and master complexity. But all software is a liability, every decision a tradeoff that prunes future possibilities. So really you want to make things as simple as possible to solve the problem at hand as that will give you more optionality on how to evolve later.

    • I’ll give a simplified example of something I have at work right now. The program moves data from the old system to the new system. It started out moving a couple of simple data types that were basically the same thing by different names. It was a great candidate for reusing a method. Then a third type was introduced that required a little extra processing in the middle. We updated the method with a flag to do that extra processing. One at a time, we added 20 more data types that each had slightly different needs. Now the formerly simple method is a beast with several arguments that change the flow enough that there are a probably just a few lines that get run for all the types. If we didn’t happen to start with two similar types we probably wouldn’t have built this spaghetti monster.

    • IMHO, it comes down to awareness/probability about the need to future proof or add defensive behavior.

      The spectrum is [YAGNI ---- DRY]

      A little less abstract: designing a UX comes to mind. It's one thing to make something workable for you, but to make it for others is way harder.

    • I saw a fancy HTML table generator that had so many parameters and flags and bells and whistles that it took IIRC hundreds of lines of code to save writing a similar amount of HTML in a handful of different places.

      Yes the initial HTML looked similar in these few places, and the resultant usage of the abstraction did not look similar.

      But it took a very long time reading each place a table existed and quite a bit longer working out how to get it to generate the small amount of HTML you wanted to generate for a new case.

      Definitely would have opted for repetition in this particular scenario.

  • DRY is misunderstood. It's definitely a fundamental aspect of code quality it's just one of about 4 and maximizing it to the exclusion of the others is where things go wrong. Usually it comes at the expense of loose coupling (which is equally fundamental).

    The goal ought to be to aim for a local minima of all of these qualities.

    Some people just want to toss DRY away entirely though or be uselessly vague about when to apply it ("use it when it makes sense") and thats not really much better than being a DRY fundamentalist.

    • DRY is misnamed. I prefer stating it as SPOT — Single Point Of Truth. Another way to state it is this: If, when one instance changes in the future, the other instance should change identically, then make it a single instance. That’s really the only DRY criterion.

      9 replies →

This was also true of Amazon's Leadership Principles. They are pretty reasonable guidelines, but in a debate, it really came down to which one you could most reasonably weaponize in favor of your argument, even to the detriment of several others.

Which maybe is also fine, I dunno :)

  • It's because they are heurists intended to be applied by knowledgeable and experienced humans.

    It can be quite hard to explain when a student asks why you did something a particular way. The truthful answer is that it felt like the right way to go about it.

    With some thought you can explain it partly - really justify the decision subconsciously made.

    If they're asking about a conscious decision that's rarely much more helpful that you having to say that's what the regulations, or guidelines say.

    Where they really learn is seeing those edge cases and gray areas

As a very senior SWE with a decent amount of eng decision making responsibility these days I still find I get so much mileage out of KISS and YAGNI that I never really think about any other laws.

So much SWE is overengineering. Just like this website to be honest. You don't get away with all that bullshit in other eng professions where your BoM and labour costs are material.

  • I always thought it would be great if every line of software (or, chose some other unit of complexity) added to the unit cost of creating each copy. Like every bolt or gasket adds a few cents the the BOM of a piece of hardware. That fictional world would have simple, fast, and higher quality software programs.

I'll propose this as the only unbreakable law: "everything in moderation", which I feel implies any law is breakable, which now this is sounding like the barber's paradox. What else does anyone propose as unbreakable?

  • >> everything in moderation

    Saying this is like saying 'pick the optimum point' without saying anything about how to find the optimum point. This cannot be a law, it is the definition of optimum.

    Note that optimum point need not be somewhere in the middle or 'inside', like a maxima. The optimum point could very well be on an extreme of the domain (input variables space).

This is doubly true in Machine Learning Engineering. Knowing what methods to avoid is just as important to know what might work well and why. Importantly a bunch of Data Science techniques — and I use data science in the sense of making critical team/org decisions — is also as important for which you should understand a bit of statistics not only data driven ML.

  • Statistics is absolutely fundamental to data science. But I’m not sure this relates to the above idea of “laws” being internally contradictory?

Most of them felt contradictory and kind of antiquated.

Reading through the list mostly made me feel sad. You can't help but interpret these through the modern lens of AI assisted coding. Then you wonder if learning and following (some) of these for the last 20 years is going to make you a janitor for a bunch of AI slop, or force you into a coding style where these rules are meaningless, or make you entirely irrelevant.