Also, (2011)
I have no idea what the website would look like without it, but I have a feeling it does more good than harm.
[1]: Example from the frontpage right now: "How Big Are Factorials?" probably got normalized to "Big Are Factorials?" originally, based off other previous manglings I have seen previously.
(Or when the auto-renamer does a funny!)
This reminds me of a talk Sandi Metz did called "All the Little Things" where she covers the Gilded Rose kata. In the talk, she reworks her solution until there's almost nothing left showing the essence of the problem being solved.
The cyclomatic complexity metric is touted at each step as a proxy for goodness of design and removal of complexity. However, a weakness of the measure itself is that it doesn't account for the control flow indirection that happens through OO method dispatch itself.
At the same time, Kevlin Henney's talk called "Gilding the Rose" takes the same kata and arrives at a far more sane solution he works up to and reveals at the end.
Short functions used to be hot. Uncle Bob used to proselytize "The first rule of functions is that they should be short. The second rule of functions is that they should be shorter than that." Now emphasizing the benefits of longer functions is pretty trendy. https://github.com/johnousterhout/aposd-vs-clean-code
This industry is pretty idiotic sometimes ¯\_(ツ)_/¯
Something like abstractions traversed during interpretation, lines of abstraction v.s. functional implementation, or logic statement dispersion.
It was hard to pin down what was abstraction vs. implementation, but it's much easier now.
The reductio ad absurdum here is that, if abstraction can just be assumed to be bad for quality and maintainability, then perhaps we should go back to hand writing machine code for non-microcoded sequential execution CPU architectures. Conversely, if that idea sounds as preposterous to you as it does to me, then you’re stuck conceding that at least some abstractions are mostly good. So then, before you can automate deciding which ones should and should not count against a code quality metric that’s computed automatically, you need to find an operational definition that can be applied deterministically.
As I recall, he concluded that there’s really no support for then-popular ideas like short functions, reducing cyclomatic complexity, avoiding explicit branch statements and loops, or TDD. (Tests yes, just not TDD.)
He made a pretty strong case that only two principles are particularly robust. One was that limiting code volume is good. The other is that working people too hard is bad.
What We Know We Don’t Know • Hillel Wayne. (2019, April 28). Hillel Wayne. https://www.hillelwayne.com/talks/what-we-know-we-dont-know/
Amen, it’s hard to push back against an opaque term (cyclomatic!) when it isn’t really a measure of goodness, it’s a measure of branching, kind of a normal thing in code.
Early on I found that code with low cyclomatic complexity was just usually extremely verbose, lots of passing this to that while avoiding the branching necessary to get something done.
And yes, you can game the metric by hiding the complexity among the confusion of objects and components.
Every indirect call is a conditional branch, where the condition can be arbitrarily far away in time and space.
Of course if you introduce new methods of dispatch and do not take them into account into a metric, you end up with something less... precise? useful? But given the primary intent and why and how the metric was created this seems a pretty trivial observation.
Now I agree it is also retarded to attempt to get only very short functions or extremely low cyclomatic complexity everywhere (even if you try to adapt it to count new kind of dispatch), because the only effect that produce is that it moves the complexity in another more abstract place we are less well equipped to manage.
"Short functions used to be hot. Uncle Bob [...]": well yes, Internet and sometimes group of people inspired ultimately by Internet and group effects can be pretty idiotic, but honestly Uncle Bob ideology was never considered serious in actual studies, and it is now even widely recognized mostly bullshit. It is just a kind of tech influencer if you want. Computer science and/or software engineering has more serious branches, where cyclomatic complexity can have its use.
Mild as this ironic passive aggressiveness is, can't imagine something like this in modern sterile corporate messaging.
every bit of humanity went with the motto
I'm sure that's still the case for some kids/parents today, but I can't imagine that being remotely common.
and
> Here’s why we think that CRAP1 is a good anti-pattern to detect. Writing automated tests (e.g., using JUnit) for complex and convoluted code is particularly challenging, so crappy code usually comes with few, if any, automated tests.
This is so wrong.
The formula uses code coverage as a fundamental metric, when in reality, a lot of people write code "correct from construction", so coverage is not even applicable. Many times too, people only care the use cases they care about work perfectly.
There are also many other reasons code is not tested, not because it's complex, but because it's simple.
High test coverage doesn't mean your code is good, but it at least reduces the rate at which you accidentally break stuff.
Disagree. Even if it isn't much work to write (which is debatable), it still carries costs: CI takes longer to run, and the tests have to be carried and maintained (and possibly updated if the code itself changes).
> High test coverage doesn't mean your code is good, but it at least reduces the rate at which you accidentally break stuff.
Sure, for the most part. But that doesn't mean "more test code" is always a good thing. Tests aren't free, even for simple code.
Are we just writing tautologies now?
It already is, ive banned unit tests via ci checks from our codebases, they were not particularly useful before LLMs and now they are a net negative.
We require int and some e2es and that does all that units do and more.
I like having both. Unit tests are a decent indicator for local development. Fast, quick to iterate on. Integration tests are slow as molasses and I can’t iterate with them when it takes 20 minutes to setup the suite and run everything. Too slow. But still useful as you say.
Playwright e2e tests are harder to maintain than pytest code, so I wrote a test just for that one case, and while I was at it, a few other input strings that seemed like they could be problems.
If the function had no test coverage at all, I probably would have tried to get it with an integration test, because that would mean the callers also weren't getting tested.
Its gotten so bad I decided to throw the baby out with the bathwater so to speak and focus on the high value places instead of trying adjudicate every individual usage
It’s a classic example of Goodhart’s Law in action. Code coverage metrics only measure what percentage of code the test suite causes to run. But it’s very, very easy to write tests that run code without actually confirming that it produces correct output for all possible inputs. And it’s very, very easy to assume that a module with 90+% code coverage also has 90+% behavior coverage, and then become complacent about reviewing the suite for proper behavior coverage.
But most developers don’t have the mathematical sophistication nor the time.
It’s not that unit testing is useless. Just good to know what their limitations are and to plan your testing strategy accordingly.
Tools like QuickCheck and Hypothesis are an interesting middle ground, though. I strongly prefer them over standard-issue unit testing for verifying algorithm implementations.
Although proof techniques such as proof repair have come a long way, it’s still impractical for a lot of scenarios.
TLA+ is great for systems design and such. Quick check style tests are awesome and a very low bar to clear from unit tests.
The one that tech tends to stumble on most often is velocity-type metrics. The problem there is that you can’t pay the bills with velocity. And velocity metrics tend to favor cheap shovelware features that cohere poorly over anything that involves having the team slow down on churning out code long enough to work out elegant solutions to subtle problems.
For Rust there's https://crates.io/crates/cargo-crap, and for Go there's https://padiazg.github.io/go-crap/