Not sure what this author is saying here. Types have nothing to do with being able to trust other systems and everything to do with the internal formal consistency of the code that you write.
It's about being consistent with yourself.
If you find yourself writing defensive checks in fulfilled promise handlers because the data you get is sometimes invalid, stop. Get up from your keyboard, go to whoever owns the endpoint, and ask them why their endpoint is returning a 200 OK response with data that doesn't live up to its spec. This is a bug in their system, not yours.
If your software crashes on bad user input, that's an error in your software.
By the way, there is distinction to make between library code that should exception/assert/abort on error and application code that should verify input and display an error to the user.
I've worked for very large companies whose massive codebases are littered with defensive checks that have piled up over many years. Invariably, I find that this is because, when the initial wave of defensive checks inevitably broke down because they guard against something that itself changes behavior again, more defensive checks were added.
All of this could have been avoided if those companies had just created a culture, from the beginning, where you force the upstream data provider to fix its issues rather than coding around them by allowing code to fail-fast rather than gracefully.
Now companies are beginning to introduce integration and unit testing across the board and type systems (again, to ensure internal consistency for systems so that other systems can depend on them) and the cruft and defensiveness is slowly going away.
To repeat the old cliché, the best defense is a good offense :-)
>had just created a culture, from the beginning...
Were it such an easy thing...
Here's how I've experienced such things:
I write naive code. Application hard fails. Test environment down - everyone cross with me.
Me: Yo upstream - fix your data please.
...no response.
So I raise a ticket to get data source fixed - send it to the backlog.
Me: Yo delivery - can we get this ticket prioritised, assigned and put into a sprint?
Delivery: Yeah... nah - cause y'know... delivery.
Me: Yo leadership - can we get a decision taken on whether or not upstream should fix their data, or whether we should just program defensively? If the former - can someone tell delivery to tell upstream?
...no response.
At this point - I give up. Write my little defensive check and admonish myself to do so consistently from now on. Commit, push... go home - sleep like babe.
Often the systems you're connecting to aren't maintained by people in the same office or company as you.
Every back end and front end system I've worked on as long as I can remember has had dependencies on third party API's. Getting bugs fixed in those APIs could sometimes take months ("change requests"), if at all.
Finding a good balance for how your system gracefully degrades is a bit of an art form.
Don't think it's unrealistic at all from within a single company.
The point about 3rd party libraries is taken but I also think if you are using a 3rd-party endpoint that publishes a specification that it then doesn't live up to (and takes months to fix) then it's not a reliable partner in whatever you're doing and is not living up to its own promises about the data its customers can expect. Like one of the other commenters here said, time to deprecate or write a wrapper around it that enforces the data contract.
I agree with this. I’m all for writing checks against values which may be optional, but I don’t litter my logic with checks against code that should provide the appropriate data.
If you’re getting bad data you need to address the issue higher up. Either through fixing your source or if you’re forced into some badly behaving library then you should at most write a wrapper around it and make that as isolated as possible. Then you should start making plans to deprecate that library.
The spec should be typed. Those other users should not just be giving you typed data, they should be giving you a type signature.
I'm referring to a general situation. You're referring to a specific situation of http apis sharing what I assume is untyped data and the typing is lost during transport. The solution is to develop protocols on top of http where the type signature isn't lost.
But the gRPC purist who wrote that endpoint while mandating that unit testing private methods is philosophically worse than genocide and using a slackbot to append “don’t write tests for the compiler” to every pull request got a huge payout to leave the company last year before sexual harassment complaints could move forward.
> If it doesn't get fixed, switch endpoints or switch jobs.
At work a large part of my job is writing integrations with other systems our customers have. As an example, just recently an integration failed due to malformed XML. This was an XML file our program receives from another system our customer has bought, based on their spec.
The error was that while the XML header said the encoding was UTF-8, the actual data was Windows-1252 encoded...
The other system had bi-yearly releases, so any fix was months away, assuming their developers even understood what the issue was... And getting our customer to switch system is out of the question, at least short-term. Meanwhile our customer wanted the integration working ASAP.
So, I'd rather code a check, make our customer happy and keep my otherwise quite nice job, than quit and leave our customer stranded.
> If it doesn't get fixed, switch endpoints or switch jobs.
Uhhhhh, this is not realistic? If I have to quit my job or switch services every time those services have a bug I'm going to be switching jobs every other week.
Anyways, I think the author is actually looking for something along the lines of gRPC, where your constraints are far stricter and easier to enforce. Option types don't even really make sense for what they're after from what I could tell.
Switching endpoints may not be option. It's probably not. Why would your company have two endpoints that do the same thing. If it's a third party service you could switch to a competitor but that's not that simple either.
Switching jobs? I mea yea you could but again that's not exactly an option for a lot of people. Particularly in the short term.
Mean while your app is stuck on a loading screen with an infinite spinner and bad reviews are flowing into the app store.
In many companies, there's also option C - fix it yourself. Any way you slice it, the bug is in the system that's reporting the wrong condition, and adding a defensive check merely papers over an issue with additional code complexity.
Sure - your program can degrade gracefully. But that should be for informational / diagnostic purposes only. It doesn't change the fact that you're using a system that is not living up to its data contract.
To make an analogy pivoting on contracts: modern programming is often not a "rule of law" environment. If you try to pretend that it is when it's not, you're just going to get a lot of pain. And you can't fix everything by yourself.
At some point, the most efficient way to push back against bad code is by validating the contract at the boundary. Then, when that breaks, you have a lot more leverage to go around and demand fixing things.
Or, better yet, use systems that don't allow contracts to be so easily broken. Static typing is an example of that.
Getting the problem fixed is about culture as much as anything else.
If there is some legacy reason something cannot be fixed and has to keep returning bad data, then yes, there is 100% a reason to have two endpoints that do the same thing... usually prefixed with /v2/ or something
It's about being consistent with yourself.
If you find yourself writing defensive checks in fulfilled promise handlers because the data you get is sometimes invalid, stop. Get up from your keyboard, go to whoever owns the endpoint, and ask them why their endpoint is returning a 200 OK response with data that doesn't live up to its spec. This is a bug in their system, not yours.