Live data from Hacker News

Applying “make invalid states unrepresentable”

kevinmahoney.co.uk

51–60 of 193 posts

Re: Applying “make invalid states unrepresentable”

#51
“Make invalid states unrepresentable” is a type of fool’s gold. You don’t care that invalid states are unrepresentable, you only ever care that a specific instance of your running program is very unlikely to enter an invalid state - and the difference between formally disallowing invalid states vs. test coverage that proves a reasonable likelihood of avoiding invalid states is huge.

The extra code and conceptual complexity spent to make type designs that disallow invalid cases is a liability, it comes with its own bugs, maintenance and huge risks of premature abstraction and brittleness in the face of changing requirements.

If it takes anything more than a simple enum-style menu of permitted options, then it’s a code small. Things like Scala case classes (especially with sealed behavior), or pattern matching against type constructors, or phantom types - these are all very bad ideas, where the costs far outweigh the benefits.

Most of the time you can just ignore enforcement of assumptions, and add a few assert statements plus lightweight unit tests and integration tests that generate an abundance of real world example cases - and achieve all the safety you need for a fraction of the code & conceptual complexity and tech debt incurred by false promises of enforcing correctness with type system designs.

Re: Applying “make invalid states unrepresentable”

#52
post #43
post #26

In general I agree that it's nice to make invalid states unrepresentable, but I'm not sure if I agree that this counts as a fundamental "invalid state". There is nothing about contracts which require that you can only have one active at the same time, or that that current one must be open ended. From a practical point of view it might be advantageous if you maintain only a single contract with a customer at all times…

Then the code will be changed. Making up business requirements is the number one reason for instant legacy code. Code is not set in concrete, you can add that flexibility later when it is needed, but making everything overly generic to make it easier to "implement new requirements" only leads to code that is hard to change in my experience. Also don't forget that this is only an example.

The YAGNI (you aint gonna need it) principle overrides the DRY principle imo.

Re: Applying “make invalid states unrepresentable”

#53
post #26

In general I agree that it's nice to make invalid states unrepresentable, but I'm not sure if I agree that this counts as a fundamental "invalid state". There is nothing about contracts which require that you can only have one active at the same time, or that that current one must be open ended. From a practical point of view it might be advantageous if you maintain only a single contract with a customer at all times…

> Multiple times I've designed systems where I've reduced the representable states to the minimum, and when some requirements change I realize I have to re-design the full system.

Yes, if requirements change, you change the design and code to support the new requirements.

Compromising the consistency and maintainability of the current design to accommodate a hypothetical future requirement change is a bad trade-off IMHO, since you can't predict the future. A requirement change may happen in a completely different direction than the one you anticipated, and then you have the worst of both worlds.

It is better to make code maintainable than making it flexible.

Re: Applying “make invalid states unrepresentable”

#54
post #53
post #26

In general I agree that it's nice to make invalid states unrepresentable, but I'm not sure if I agree that this counts as a fundamental "invalid state". There is nothing about contracts which require that you can only have one active at the same time, or that that current one must be open ended. From a practical point of view it might be advantageous if you maintain only a single contract with a customer at all times…

> Multiple times I've designed systems where I've reduced the representable states to the minimum, and when some requirements change I realize I have to re-design the full system. Yes, if requirements change, you change the design and code to support the new requirements. Compromising the consistency and maintainability of the current design to accommodate a hypothetical future requirement change is a bad trade-off I…

> since you can't predict the future

That's one of those statements that makes sense, but is often not true. Very rarely does a client comes to me with a feature request that does require a pretty significant design change, but most of the time they're changes that were foreseen.

Using this current article as an example, I love the way that they're storing the intervals to guarantee that they can't overlap. That's awesome! What I would likely end up doing, though, is use that as the underlying representation but still return individual interval objects through the query API with a start and end date on each interval. That way, if the "only one at a time" rule changes, the changes required are localized.

Re: Applying “make invalid states unrepresentable”

#55
post #27

While this is great if you know exactly what you want to achieve, it does “lock you in” those constraints on a more fundamental level. More times than I can count I’ve seen business requirements change to require those “unrepresentable” states, and since you’ve now designed you whole data model around it you need to add awful hacks to make it work. The timeline example is actually very telling. A lot of times you’d a…

Additionally, turning (startDate, endDate) into a set of dates will make the code more complex in some places. Before: SELECT event FROM events WHERE endDate After: Whatever additional complexity you add to your codebase to query end dates.

It's not that much more complex: if you do

    SELECT event FROM events WHERE startDate 
then all but one of the results (the one with the greatest `startDate`) will also have an implicit end-date prior to 2021.

Re: Applying “make invalid states unrepresentable”

#57
post #52
post #43

Earlier quoted context omitted.

Then the code will be changed. Making up business requirements is the number one reason for instant legacy code. Code is not set in concrete, you can add that flexibility later when it is needed, but making everything overly generic to make it easier to "implement new requirements" only leads to code that is hard to change in my experience. Also don't forget that this is only an example.

The YAGNI (you aint gonna need it) principle overrides the DRY principle imo.

[deleted]

Re: Applying “make invalid states unrepresentable”

#58
post #43
post #26

In general I agree that it's nice to make invalid states unrepresentable, but I'm not sure if I agree that this counts as a fundamental "invalid state". There is nothing about contracts which require that you can only have one active at the same time, or that that current one must be open ended. From a practical point of view it might be advantageous if you maintain only a single contract with a customer at all times…

Then the code will be changed. Making up business requirements is the number one reason for instant legacy code. Code is not set in concrete, you can add that flexibility later when it is needed, but making everything overly generic to make it easier to "implement new requirements" only leads to code that is hard to change in my experience. Also don't forget that this is only an example.

[deleted]

Re: Applying “make invalid states unrepresentable”

#59
post #26

In general I agree that it's nice to make invalid states unrepresentable, but I'm not sure if I agree that this counts as a fundamental "invalid state". There is nothing about contracts which require that you can only have one active at the same time, or that that current one must be open ended. From a practical point of view it might be advantageous if you maintain only a single contract with a customer at all times…

> There is nothing about contracts which require that you can only have one active at the same time, or that that current one must be open ended.

Agreed and yet for many systems, that flexability still eludes.

One example I personally experienced was changing a phone contract. The contract had run for many years, so could be cancelled any time with one months notice. The new plan and contract was much better and yet a limitation played out doing this. Ended up that the systems at the telco was unable to activate the new contract until the old contract had ended. Whilst a new contract could be physically signed in a shop with a start date of the day of signing, and logged into the system. The provisioning backed was unable to activate it until the old contract had ceased as you can't have two contracts for the same phone number.

That I do believe is a case of - whilst some things can run in parallel, others are locked to a single dependant resource.

But every rule has an exception, it is with good design that you limit those exceptions impact.

Re: Applying “make invalid states unrepresentable”

#60
post #49

I like the concept but I’ve seen a fair few examples of where the developers and users clearly had differing opinions about which states are invalid! Dates are a rich vein of examples. Some users will happily consider “25th December” to be a date, without any year, because it might be the name of a folder in which they store their Christmas stuff. More seriously, genealogists or historians may want to record “25th De…

Also in genealogy:

- estimated dates

- calculated dates (e.g. someone was 30 in 1870, so he was born in "calculated 1840")

- unreadable or unavailable months or days (typically recorded as 1980-00-13)

- time ranges with all of the above as boundaries e.g. "after 1760-03-00 and before calculated 1800"

- plainly incorrect dates, but that's what the document says (1865-02-30)

- no dates (some software tries to enforce putting in some data in for whatever reason)

- dates with unknown calendar

Post reply on HN