The compiler is your best friend
91–100 of 149 posts
Re: The compiler is your best friend
#92Earlier quoted context omitted.
> debug_assert!() (only run in debug builds) debug_assert!() (and it's equivalent in other languages, like C's assert with NDEBUG) is cursed. It states that you believe something to be true, but will take no automatic action if it is false; so you must implement the fallback behavior if your assumption is false manually (even if that fallback is just fallthrough). But you can't /test/ that fallback behavior in debug…
I hear you, but sometimes this is what I want. For example, I’m pretty sure some complex invariant holds. Checking it is expensive, and I don’t want to actually check the invariant every time this function runs in the final build. However, if that invariant were false, I’d certainly like to know that when I run my unit tests. Using debug_assert is a way to do this. It also communicates to anyone reading the code what…
Re: The compiler is your best friend
#93Earlier quoted context omitted.
I hear you, but sometimes this is what I want. For example, I’m pretty sure some complex invariant holds. Checking it is expensive, and I don’t want to actually check the invariant every time this function runs in the final build. However, if that invariant were false, I’d certainly like to know that when I run my unit tests. Using debug_assert is a way to do this. It also communicates to anyone reading the code what…
But how do you test the recovery path if the invariant is violated in production code? You literally can’t write a test for that code path…
If there's a known bug in a program, you can try and write recovery code to work around it. But its almost always better to just fix the bug. Small, simple, correct programs are better than large, complex, buggy programs.
Re: The compiler is your best friend
#94Earlier quoted context omitted.
Such comments rot so rapidly that they're an antipattern. Such assumptions are dangerous and I would point it out in a PR.
Do you not make such a tacit assumption every time you index into an array (which in almost all languages throws an exception on bounds failure)? You always have to make assumptions that things stay consistent from one statement to the next, at least locally. Unless you use formal verification, but hardly anyone has the time and resources for that.
Yes, which is one reason why decent code generally avoids doing that.
Re: The compiler is your best friend
#95> How many times did you leave a comment on some branch of code stating "this CANNOT happen" and thrown an exception? Did you ever find yourself surprised when eventually it did happen? I know I did, since then I at least add some logs even if I think I'm sure that it really cannot happen. I'm not sure what the author expects the program to do when there's an internal logic error that has no known cause and no defini…
> At some level, the simplest thing to do is to give up and crash if things are no longer sane. The problem with this attitude (that many of my co-workers espouse) is that it can have serious consequences for both the user and your business. - The user may have unsaved data - Your software may gain a reputation of being crash-prone If a valid alternative is to halt normal operations and present an alert box to the us…
Re: The compiler is your best friend
#96But it doesn't always make sense -- e.g. a language for large-scale linear algebras, or a language for web GUIs might be not the best to compile itself.
Re: The compiler is your best friend
#97Earlier quoted context omitted.
But how do you test the recovery path if the invariant is violated in production code? You literally can’t write a test for that code path…
There is no recovery. When an invariant is violated, the system is in a corrupted state. Usually the only sensible thing to do is crash. If there's a known bug in a program, you can try and write recovery code to work around it. But its almost always better to just fix the bug. Small, simple, correct programs are better than large, complex, buggy programs.
Correct. But how are you testing that you successfully crash in this case, instead of corrupting on-disk data stores or propagating bad data? That needs a test.
Re: The compiler is your best friend
#98> How many times did you leave a comment on some branch of code stating "this CANNOT happen" and thrown an exception? Did you ever find yourself surprised when eventually it did happen? I know I did, since then I at least add some logs even if I think I'm sure that it really cannot happen. I'm not sure what the author expects the program to do when there's an internal logic error that has no known cause and no defini…
Heh, recently I had to fix a bug in some code that had one of these comments. Feels like a sign of bad code or laziness. Why make a path that should not happen? I can get it when it's on some while loop that should find something to return, but on a if else sequence it feels really wrong.
Re: The compiler is your best friend
#99> A common pattern would be to separate pure business logic from data fetching/writing. So instead of intertwining database calls with computation, you split into three separate phases: fetch, compute, store (a tiny ETL). First fetch all the data you need from a database, then you pass it to a (pure) function that produces some output, then pass the output of the pure function to a store procedure. Does anyone have a…
> there's no way to "fetch all the data" up front. this is incorrect I assume there's more nuance and complexity as for why it feels like there's no way. Probably involving larger design decisions that feel difficult to unwind. But data collection, decisions, and actions can all be separated without much difficulty with some intent to do so. I would suggest caution, before implementating this directly: but imagine a…
Sometimes you might need to operate on a result from an external function, or roll back a whole transaction because the last step failed, or the DB could go down midway through.
The theory is good, but stuff happens and it goes out the window sometimes.
Re: The compiler is your best friend
#100> A common pattern would be to separate pure business logic from data fetching/writing. So instead of intertwining database calls with computation, you split into three separate phases: fetch, compute, store (a tiny ETL). First fetch all the data you need from a database, then you pass it to a (pure) function that produces some output, then pass the output of the pure function to a store procedure. Does anyone have a…
That said:
> It pulls customer metadata, checks the customer type, and then based on that it computes their latest usage charges, and then based on that it may trigger automatic balance top-ups or subscription overage emails (again, depending on the customer type).
So compute those things, and store them somewhere (if only an in-memory queue to start with)? Like, I can already see a separation between an ETL stage that computes usage charges, which are probably worth recording in a datastore, and then another ETL stage that computes which top-ups and emails should be sent based on that, which again is probably worth recording for tracing purposes, and then two more stages to actually send emails and execute payment pulls, which it's actually quite nice to have separated from the figuring out which emails to send part (if only so you can retry/debug the latter without sending out actual emails)