1. 62
  1.  

  2. 5

    Contiguous, non-overlapping splits can simply be represented by a set of dates (Set Date):

    Actually this has some issues too and still causes “invalid state”. Given a set can be empty, or have 1, or more elements, here are two invalid states:

    {}
    { 1990-01-01 }
    

    These are not ranges.

    You would have to enforce a Set of length > 1.

    …How do you do this in your language? :)

    AFAIK only dependently typed languages can do this. Not relevant.

    They go on about Fixed vs Default contracts, and simply say “well if we remove Default contracts, it all works out!”.

    …Ok, now what if multiple, non-overlapping contract types are required? Where’s the solution to that more interesting problem? They’ve just taken the problem and reduced it to the original they presented.


    I’m not a fan that the author is saying to apply a technique but then is just presenting a way to prevent an invalid state (and not succeeding, as we see) in a very specific problem.

    1. 13

      Whether those values are invalid depends on the application. They could be interpreted as a single infinite period and two infinite periods respectively.

      But anyway, that’s beside the point, because the goal is to reduce invalid states not eliminate them entirely. The less permissive representation is the better one.

      Constraints can be enforced at the database level and application level too, but I consider it good practice to do so at the representation level if possible. It usually results in simpler code, as demonstrated in the article.

      1. 3

        the goal is to reduce invalid states not eliminate them entirely

        Why not eliminate them entirely? You want invalid state?

        The less permissive representation is the better one.

        Not true, it depends on the problem.

        It usually results in simpler code, as demonstrated in the article.

        You presented a single case. There are hundreds of thousands of cases - how can you conclude “usually” then?…??


        Edit: I want to make absolutely clear, for what it’s worth, your article is not written bad, I’m just being critical about the actual content :) In no way do I mean to discourage. I’m just challenging what’s been written! :)

        1. 11

          Why not eliminate them entirely? You want invalid state?

          Clearly nobody wants invalid states, but as you noted, without dependent typing eliminating certain invalid states can get tricky.

          The less permissive representation is the better one.

          Not true, it depends on the problem.

          It usually results in simpler code, as demonstrated in the article.

          You presented a single case. There are hundreds of thousands of cases - how can you conclude “usually” then?…??

          I feel like you are being unnecessarily pedantic here. Of course it depends on the problem and of course there are situations where this technique will not result in simpler code (dependent types may be “simple” to use but have extreme complexity in the compiler).

          This article lays out a great technique and gives good examples for it. I think it is a great intro to this style of programming and thinking about problems for people not familiar with it.

          Perhaps your argument would be made better by laying out where the shortcomings of this technique are. In which situations will this result in more complicated code? In which situations is a more permissive representation desirable? I think these are valid points but lets actually dive into them rather than surface-level critiques.

          One instance in which a more permissive representation is desirable is possibly in “enterprise code.” The idea is that business partners will have so many conflicting and changing requirements that designing the system to have as little “baked in” constraints is actually best. This means have as flexible as a representation as possible, and having the constraints controlled by configuration.

          Of course this has a number of trade-offs, and personally I hate the enterprise style of programming and am critical of its actual use. But I think it is an important reminder that programming, at its core, serves a business function – and sometimes that business function can change rapidly. So if your constraints are too baked in you can screw yourself over later and lead to far more complexity.

          1. 1

            I feel like you are being unnecessarily pedantic here. Of course it depends on the problem and of course there are situations where this technique will not result in simpler code (dependent types may be “simple” to use but have extreme complexity in the compiler).

            It’s written in a factual tone, that’s the only reason why. “The less permissive representation is the better one.”, how does that not sound like a fact?

            “This article lays out a great technique” <- The technique of just thinking about how to represent something?…

            “Gives good examples”, it’s a single example!

            Perhaps your argument would be made better by laying out where the shortcomings of this technique are. In which situations will this result in more complicated code? In which situations is a more permissive representation desirable? I think these are valid points but lets actually dive into them rather than surface-level critiques.

            I did lay it out. And the point is, is that you cannot factually say “usually X happens” while presenting a single case. Why does it matter which situations? It’s enough to say there are situations. You gave one.

            style of programming

            It’s really not programming but a style of engineering (or lack-of). 100% agree with what’s said in the last paragraph. :)

            1. 8

              “This article lays out a great technique” <- The technique of just thinking about how to represent something?…

              Yes that is clearly the technique. You might argue it is basic, sure, but there are a number of OO-heavy programmers I know who would benefit from learning this mindset.

              “Gives good examples”, it’s a single example!

              It is very clearly two examples, although the second one is, as you’ve pointed out, a bit weaker. Even one example I think conveys the point though.

              I did lay it out. And the point is, is that you cannot factually say “usually X happens” while presenting a single case. Why does it matter which situations? It’s enough to say there are situations. You gave one.

              I think it is clear the author stated their opinion, and not a fact. Everything written is from someones opinion, and unless someone states “it is a fact X” then they aren’t claiming that it is a fact.

              It matters which situations because that helps others understand and learn! The author believes X, you don’t believe X. It is not beneficial to anyone for you to say “X is wrong.” This is a discussion forum where people come to learn about new ideas.

              The author presents an idea which is valid and useful in certain situations, and troublesome in others. Lets discuss those situations and try to find patterns. Maybe in the enterprise world, constraining data representation can bite you in the ass. In the security world, constraining data representation is the difference between an innocuous bug and one that brings down the whole system. That is the discussion I want to read!

              1. 0

                This is a discussion forum where people come to learn about new ideas.

                So uh, we should not question or challenge anything? Like what the heck?… We should not think critically now?

                In the security world […]

                Oh come on. This is not just about constraining data representation. This is about being precise about your specification, and then using the correct tools to make sure that specification can be realized.

                It is not beneficial to anyone for you to say “X is wrong.”

                And I gave examples of why it was wrong. You have not told me why it’s wrong to give proof of something that is wrong.

                And it definitely doesn’t read like an opinion piece, but ok.

                1. 5

                  This is a discussion forum where people come to learn about new ideas.

                  So uh, we should not question or challenge anything? Like what the heck?… We should not think critically now?

                  Clearly I’m not saying that.

                  Oh come on. This is not just about constraining data representation. This is about being precise about your specification, and then using the correct tools to make sure that specification can be realized.

                  I don’t understand your point here. The article is entirely about constraining data representation to reduce bugs. Let us take the first example. The two ways of representing the data – from a specification standpoint – are identical. The “view” built on top of them is the same. This article is explicitly arguing that constraining the representation is the tool to make that implementation of the specification less buggy, and less prone to bugs in the future.

                  And I gave examples of why it was wrong. You have not told me why it’s wrong to give proof of something that is wrong.

                  Your first comment is good, and I like it because it brings up the fact that there are limits to how much we can constrain the representation. Your second comment in response felt like it was pedantic for the sake of it and didn’t really add to the discussion. I didn’t mean for this to become a back-and-forth about this, and I was hoping to pivot your comment into something that contributed more readily to the discussion and get your insights into some examples where this technique is a bad idea.

                  I in no way want to limit thinking critically or questioning the advice of something, and please feel free to point out where my writing comes across that way because it is not my intention.

          2. 6

            In no way do I mean to discourage.

            I have nothing against well intentioned criticism and I’m not discouraged, but I do think you could be a little more charitable in your interpretation. :) I agree with the other comment that your arguments come across as pedantic.

            It seems like you’re taking issue with my certainty. In software engineering almost everything is ‘it depends’ or a trade-off. I don’t know if you write, but I find if I taint every statement with that kind of uncertainty it ruins the flow of the text. I rely on the reader understanding that, in this context, I am talking about rules of thumb and opinions, not mathematical truths.

            1. 1

              I think the issue is a lot of your text comes off as factual, that’s all. It’s all good.

              1. 2

                From the sidelines here I agree with you. The article title does say Applying “Make Invalid States Unrepresentable”, and even starts with Here are some real life cases of applying one of my favourite principles.

                But then the text goes on and makes pretty wide-ranging assumptions sounding, as you say, factual. I think it does not make such a strong point about “making invalid states unrepresentable”. To me, it sounds more like “I have a solution here that does not actually solve my specifc problem, so I will adjust the solution so that it does.I feel like it’s obvious that you have an invalid state at some point, because it was designed to be invalid. Then we show a solution that apparently is correct (minor details aside). Nothing to do with making state representable because the starting state was broken or unfinished.

                At least that’s what it seemed to me to be the case.

                1. 3

                  Most of the article IS factual. There is opinion in the last section. If you disagree, quote the part of the article you have an issue with and I’ll consider changing it.

                  What both of you may be missing is that these aren’t invented examples to prove a point, this was how the code was actually written. In the second case, the suggested change would have prevented actual observed production bugs.

                  1. 1

                    That’s the thing. I’m not saying the article isn’t factual, it is. But then the conclusions seem to follow as a fact as well. Which it can’t be, I think, because it’s anecdotal. So again, as mentioned, what I understood from the article, is not that you had “invalid state representable” then applied some technique to make it not representable. It’s more of a “I had am unfinished software”, or “i had software which made invalid state valid, and therefore representable” which got fixed.

                    On the other hand, it’s just what I took from the post. There’s a high chance that I simply misunderstood it. Similar to the other commenter.

        2. 6

          I don’t think you need dependent types here. Just define a custom Set whose constructor requires at least two items or it throws an error.

          1. 9

            Or better yet, make it a two parameter constructor.

            1. 1

              Yeah, that’s what I meant. Should have been clearer :)

            2. 2

              I mean as long as you add that logic anywhere it should work, yep! I was thinking more as a compile-time check but 100% you can do it at run-time.

              1. 1

                Yeah, I meant you could get a compile time check off that constructor. Require two args or a tuple of length 2.

            3. 4

              Okay, so they failed to make all invalid states unrepresentable.

              So what?

              The examples given there still manage to eliminate entire classes of errors, and they do so without using any advanced techniques — not even tagged unions. It was simply about eliminating redundancy in the data representation. Hence what I believe is the main idea of this article: less redundancy means less room for error.

              I’ve personally applied this for nearly as long as I can remember: since mutable state tends to be the bane of my existence, I try to minimise it. Whenever a bit of computation can save me the trouble of storing a piece of data, I do the computation instead of the storage. That way my data runs less risk of going out of sync.

              In my opinion, this article is excellent advice for beginners.

              1. 3

                The examples given there still manage to eliminate entire classes of errors, and they do so without using any advanced techniques — not even tagged unions. It was simply about eliminating redundancy in the data representation. Hence what I believe is the main idea of this article: less redundancy means less room for error.

                That’s why the relational database world cares so much about normalization, after all.

            4. 4

              Great article! It wasn’t said explicitly, but a tree based set for range queries would be nice way to handle the first solution. Whenever I think sets I typically think unordered hash sets, which might be a bit more cumbersome to work with.

              1. 2

                If this improvement seems obvious to you, you may wonder how the original design happened in the first place.

                It is easier to analyse and design in retrospect once all requirements are in place. I suspect that the implementation was subject to “creeping requirements” to some degree. That is, some of the functional requirements and those all important constraints, were only discovered and implemented over time. Was that the case?

                In my view, it is better to implement constraints in executable code, rather than structurally. As complexity grows, it will become increasingly difficult to model constraints structurally, either within a data-model/structure or a class network/hierarchy designed using UML type thinking.

                An example of structural torment is Eric Lippert’s modeling of Wizards and Warriors [1], which attempts to model functionality and constraints structurally.

                [1] https://ericlippert.com/2015/04/27/wizards-and-warriors-part-one/

                1. 1

                  Was that the case?

                  In the second case the former representation was chosen over the latter right from the start. The idea of structurally preventing invalid states was not well understood, despite my efforts.

                  it is better to implement constraints in executable code, rather than structurally.

                  You don’t think the cases presented are clear improvements?

                  Wizards and Warriors

                  This reads more like a criticism of inheritance and OO design to me! :)

                  1. 2

                    You don’t think the cases presented are clear improvements?

                    Those certainly are improvements. However, my point was more generally that as complexity grows, it will become increasingly difficult to model additional constraints using a structural approach.

                    This reads more like a criticism of inheritance and OO design to me!

                    Absolutely not. Just that the attempt to model functionality and constraints solely in terms of a structural class hierarchy/network is problematic, again as complexity grows and changes are required, as is the case in that series of articles. That many don’t know where to put constraints in code in an object-oriented model of a problem or system, doesn’t mean that object-oriented design is “flawed”.

                2. 1

                  I like this idea, and your way of expressing it. It’s a sorta silent concept to have mechanical sympathy for the bookkeeping, an implicit structuring that omits redundant details and disallows certain complexities. An a priori agreement to not let the data complicate things.

                  I think you can use Set[n] of Integer to model intervals fine, yes, we just have to play a version of the game where Set[0] means “today only” or something (maybe never, maybe forever.. we just have to agree) and Set[1] means since-then-going-forward, &c. It would, as a matter of course, be a slight mistake to write code for Set[n] when you really don’t have a good idea what Set[-1] means, you would just have to find out what all the -extensions to your input lead to. I think your code is clearest written as Set[2+] or something, as I think indirection is saying.

                  Games like these can be taken as far as compression, sure, but what I like is the power to uncomplicate the structure, directly. Disambiguate at design time, maybe compile time, not run time. I haven’t thought much about dependent types in a while, but I guess I might be doing that today..

                  1. 1

                    This certainly is a good idea, but like a lot of the good ideas, it’s important to figure out where to draw the line.

                    Taken to an extreme, we would need to produce formal specifications so your program won’t compile if it could possibly reach an invalid state. While still a valid approach for some scenario, it would definitely be too costly for most cases.