LOC is useful here not because it's a metric for output but because it's a metric for _understandability_. Reviewing 200 lines is a very different workload than reviewing 2000.
That's assuming the 200 lines are logical and consistent. Many of my most frustrating LLM bugs are caused by things that look right and are even supported by lengthy comments explaining their (incorrect) reasoning.
But it is orthogonal to the question of evaluating 2000 lines of AI code vs 200 lines of human written code. Either the human or the AI could produce idiomatic code for either language, given sufficient training data in the AI’s corpus for the language.
My guess is that the first one is much quicker to review, for a human equally fluent in both languages.
The point is that LOC is never a good metric for any aspect of determining the quality of code or the coder because it ignores the nuance of reality. It's impossible to generalize because the code can be either deceptively dense or unnecessarily bloated. The only thing that actually matters is whether the business objective is achieved without any unintended side effects.
> The only thing that actually matters is whether the business objective is achieved without any unintended side effects.
Objectives change; timeliness matters. The speed at which you deliver value is incredibly important, which is why it matters to measure your process. Deceptively dense is what I’d call software engineers who can’t accept that the process is actually generalizable to a degree and that lines of code are one of the few tangible things that can be used as a metric. Can you deliver value without lines of code?
> Objectives change; timeliness matters. The speed at which you deliver value is incredibly important, which is why it matters to measure your process.
This assumes that shorter code is faster to write. To quote Blaise Pascal, "I would have written a shorter letter, but I did not have the time."
> Can you deliver value without lines of code?
No, but you can also depreciate value when you stuff a codebase full of bloated, bug-ridden code that no man or machine can hope to understand.
You seem determined to misinterpret. I’m not talking about LOC as a measure of productivity. The ratio of LOC needing review to the capacity of reviewers (using how many LOC can be read/reviewed over the sampling period) is what’s being discussed. Agentic AI/vibe coding has caused that ratio to increase and shows a bottleneck in the SDLC. It’s a proxy metric, get over yourself.
“All models are wrong, some are useful”. What’s not useful is constantly bitching about how there’s no way to measure your work outside of the binary “is it done” every time process efficiency is brought up.
Yes, reading this back, I definitely veered off-topic. I apologize. I still don't think that you can say how much time it will take to review code based on how many lines of code are involved, but my argument was not well crafted. I just hope that others can learn something from our discussion. Thank you for being patient with me, and I hope you have a good day! :)
I have worked with code where 1000s of lines are very straightforward and linear.
I’ve worked on code where 100 lines is crucial and very domain specific. It can be exceptionally clean and well-commented and it still takes days to unpack.
The skills and effort required to review and understand those situations are quite different.
One is like distance driving a boring highway in the Midwest: don’t get drowsy, avoid veering into the indistinguishable corn fields, and you’ll get there. The other is like navigating a narrow mountain road in a thunderstorm: you’re 100% engaged and you might still tumble or get hit by lightning.
The number of bugs tends to be linear to lines of code written meaning fewer lines of code for the same functionality will have fewer bugs.
So I’m pretty skeptical that reviewing 2000 lines of code won’t take any more time than reviewing 200 lines of code.
Furthermore how do you know the AI generated lines are the open highway lines of code and not the mountain road ones? There might be hallucinations that pattern match as perfectly reasonable with a hard to spot flaw.
> The number of bugs tends to be linear to lines of code written meaning fewer lines of code for the same functionality will have fewer bugs.
It depends on the code. If you’re comparing code of the same complexity then, sure, 2000 lines will take longer than 200.
I was comparing straight linear code to far more complex code. The bug/line rate will be different and the time to review per line will be different.
> Furthermore how do you know the AI generated lines are the open highway lines of code and not the mountain road ones?
Again, it depends on the code. Which was my point.
Linear code lacks branches, loops, indirection, and recursion. That kind of code is easy to reason about and easy to review. The assumptions are inherently local. You still have to be alert and aware to avoid driving into the cornfields.
It’s a different beast than something like a doubly-nested state machine with callbacks, though. There you have to be alert and aware, and it’s inherently much harder to review per line of code.
sccache caches at the per-rustc invocation level, whereas we cache at the package level. In practice, this means we get more cache hits and our cache hits take less overhead to load. You can see the benchmarks in our CI!
Monads are a generalization of Promises. Each type in Monad defines their own `.then` in a different way. For promises, `.then` is defined as "run this function once you have this deferred value from the last promise". For optionals (`Maybe`), `.then` is defined as "run this function if the last optional had an actual value". For Either, `.then` is defined as "run this function if the last Either returned Right, otherwise early-return with the value from Left" (this is functional early-return, basically).
This is the first explanation of monads I've heard that makes intuitive sense to me and feels like it sufficiently captures the point. Unless I come back in a few hours to see a bunch of replies from uber-haskellers saying "no that's not what a monad is at all," then I'll consider my search for a good monad explanation to finally be over.
You need to be able to "wrap" values and then also "wrap" functions in the way you expect. That's literally it.
Btw, the list monad example is stupid imo and borderline misleading. The promise/nullable/Either examples are better. you "wrap" a function by putting it as the only value in a list, and "map" pretty much acts as your function wrapper, but technically this you need to jump through a couple hoops to make it monadic, and I'm just not sure the metaphor is helpful here
For some monads the "wrap" gets awfully metaphorical (for State it's a function that produces the value and updated state, for Const there is no value, etc) but I don't think that's actually a problem, just a thing to be aware of. There is certainly no expectation that you can actually get your hands on the thing.
A bigger issue is that you're missing a piece. If you can "wrap" values and "wrap" functions such that they operate on wrapped values, you (probably) have a functor. To be a monad you also need to have the ability to turn multiple layers of wrapping into one layer of wrapping. For lists, that's "flatten".
I said "probably" above because there are rules these pieces need to follow to behave well. They're pretty simple but I don't think we need to dig into them at this level of discussion.
Agree about the list monad. I have never used it in actual code. I always felt it was so arbitrary, and "implicit", in the sense that if you haven't learnt beforehand that it makes "joins", then it's certainly not obvious that it should work that way
λ> do { a <- [1,2]; pure a; }
[1,2]
-- oh so it returns the list unchanged
λ> do { a <- [1,2]; b <- [3,4]; pure a; }
[1,1,2,2]
-- no what is this spooky action at a distance
λ> do { a <- [1,2]; b <- [3,4]; pure [a]; }
[[1],[1],[2],[2]]
-- ...
λ> do { a <- [1,2]; b <- [3,4]; c <- []; pure [a]; }
[]
λ> do { a <- [1,2]; b <- [3,4]; c <- [5]; pure [a,b,c]; }
[[1,3,5],[1,4,5],[2,3,5],[2,4,5]]
This is misleading IMO because Promise is a particular kind of monad but in general monads don’t necessarily have anything to do with asynchrony. It is a useful example though because some people are more familiar with the API of promises.
I might have some inaccuracies in how I state this since I’m not from a functional programming background, but I think of monads as an abstraction of chaining functions over a value such that each returned value can specify what further transformations it supports, and particularly in such a way that generic transformations (like sequence reversal) can easily be applied, and errors or empty values can be accounted for within the control flow.
I think sometimes people call this a “fluent API”, but I would never call it “a generalization of callbacks” or of promises.
Anyway there’s a much more mathematically precise way of stating it but this is my intuitive caveman way of thinking about it.
Yup, this was similar to our experience as well. If publishing is still a source of toil for you, feel free to reach out at eliza@attunehq.com - we'd love to see if we can do any dev work or hosting pro bono for your open source projects.
I'm not familiar with mise and ubi, but these look like they're tools that the end user runs to install binaries. In contrast, we're building something that sets up a server so that end users can use their existing `apt-get install`.
Yeah, multi-distro and multi-arch is something on our roadmap! We're especially interested in automating the more annoying OS-specific parts of releasing, like MacOS notarization.