One of the things i miss the most from developing in ruby is the code quality instrumentation: metric-fu, reek, rubocop and lots of other gems that allowed us (in a previous ruby shop) understand and monitor the quality of our code like ABC, Cyclomatic complexity, duplicated code, etc).
Now in Typescript, I haven't found a lot of tools to calculate,visualize and track these stats. Metric-fu was amazing.
You can get a lot of mileage out of ESLint + typescript-ESLint + various other plugins. It's more of a "choose your own adventure" kind of experience, but the defaults are a good start, and if you're looking for cyclomatic complexity and all that jazz, there's various plugins that can calculate that. And the typescript ESLint plugin is great because it uses the types as part of the hints, which is great for finding places where you no longer need optional chaining, or where a condition is always true/false because you forgot what types things were.
That said, I tend to take a much more lean approach to linting these days. I try to only include lints that are actionable, likely to lead to real bugs, and have no false positives. Everything else feels like it's better handled in code review. If you don't know why your function is too complex and difficult to read, I don't want you to rewrite it just to satisfy a robot that's following arbitrary rules! I want to be able to have a discussion with you about proper techniques for splitting apart code.
But there's definitely people who go the other way entirely and build incredibly strict ESLint configurations, so looking for those might help you if that's what you want.
It’s been quite some time since I’ve used Rubocop but I did fight an uphill battle to get it into place. One of the things I had a difficult time explaining was that rules subtly interact with each other. When one rule makes a change, it might simplify something such that another rule can then also make a change. Once I enabled enough rules, there were some pretty impressive chains that were whittled down to almost nothing… but the same application of those rules elsewhere caused hemming and hawing by the naysayers.
I think the best thing that these tools give, above all else, is a way to settle petty opinions in a team. It’s why there is value in rules that either conflict with each other or are configurable so as to meet the expectations of a given team.
Yep. I introduced Rubocop at a past gig… only to watch my colleague write worse code playing whackamole trying to adhere to the rules.
I kept telling him that the whackamole is a sign to rework things at a higher level - that the Rubocop rule catches symptoms, and that he needed to address the cause, but…
PS - to be clear, I love Rubocop and swear by it. In that circumstance, I needed to introduce it differently, is all.
Yeah this is huge. So often I get my PRs rejected because one dev has their own personal preference for formatting something. With Rubocop I can tell them to go submit a PR themselves to make that preference a rule and we can all discuss it and enforce it / autoformat it going forward. But for now, it won't be blocking my change going out.
Hopefully rubocop-rails-omakase [1] will pull in stuff like this. I've gone from custom Rubocop rules in various projects to just using rubocop-rails-omakase, which finally feels like a consensus of sorts. I think Standard [2] is harmfully redundant, personally.
Rubocop is one of the worst Ruby tools there is. Flog and reek are also unhelpful, but Rubocop is style nonsense that doesn’t belong in a linter. The ESLint community used to be big on useless opinion based rule overload, until everyone realized how bad of an idea it was.
Nice. Now we can probably ask to move there half of the too nosy rubocop rules from the main rubocop repo. And then to never install rubocop-obsession!
Right? I seem to be the only high-level SWE at my company who hates RuboCop. Some of it's fine (e.g. no globals, use x.count { ... } instead of x.select { ... }.count). But so many of the rules are obsessed with trivial details like the "right" style of blocks to use (do vs {}) or "" vs '' for strings. Pointless busy work. I think the dumbest is requiring "def (x=true)" over "def (x = true)". Seriously, why could that possibly matter??
I see that as: it doesn't matter, therefore let's do one consistent thing and you'll never get a person saying "this should be x=true". RoboCop forces it one way and I never have to have a syntax/formatting discussion again.
You may think it really really doesn't matter, to the point that whatever form should be accepted. But then, would you accept my "def foo (x =true ,y= false )"? I've seen enough PRs with that kind of code to know that people will submit it one day.
> I think the dumbest is requiring "def (x=true)" over "def (x = true)". Seriously, why could that possibly matter??
For Python, I use ruff to autoreformat my code into a consistent style. The CI pipeline fails the PR if the style is violated it. Fixing it is a one-liner. Actually, if you use VSCode’s ruff extension, a zero-liner, since it auto-formats on save.
For Java, I use Diffplug Spotless for a similar experience.
One advantage of forcing the code to be auto-formatted, is it makes diffs cleaner - you eliminate diffs where someone just changed the code style, since the code style never changes.
`””` vs `''` isn’t just a style rule. In Ruby there are performance implications with double quoted strings. Strings in double quotes are interpolated for variable substitution, whereas strings in single quotes are processed literally, no variable replacement overhead. Is it going to matter for most code? Probably not, but good to know nonetheless imo.
Why does it matter when there is auto-fix-on-save? I just write whatever, hit ctrl+s and the code is fixed. The result: The code is "fixed" based on whatever rule and it doesn't cost me a thing. I don't think about it and I don't lift a finger.
But I do personally appreciate the tidiness of code-consistency.
Most of these trivial rubocop rules autoformat so it doesn't bother anyone. The ones that don't are usually odd things like code that doesn't run or things with the same name, which can't be safely autofixed.
It does help at some margin to make code more readable. Humans just aren't that great at reading and understanding code. It has been said: "Write for humans, not computers because computers will understand what you wrote no matter how badly written". So if you're writing code on a team, do your co-workers and your future-self a favour and write code that's easy to read. It won't run any better or with fewer bugs, but it will be easier to understand later, easier to be understood by others, and "wishful thinking here": easier to debug.
One of the things i miss the most from developing in ruby is the code quality instrumentation: metric-fu, reek, rubocop and lots of other gems that allowed us (in a previous ruby shop) understand and monitor the quality of our code like ABC, Cyclomatic complexity, duplicated code, etc).
Now in Typescript, I haven't found a lot of tools to calculate,visualize and track these stats. Metric-fu was amazing.
You can get a lot of mileage out of ESLint + typescript-ESLint + various other plugins. It's more of a "choose your own adventure" kind of experience, but the defaults are a good start, and if you're looking for cyclomatic complexity and all that jazz, there's various plugins that can calculate that. And the typescript ESLint plugin is great because it uses the types as part of the hints, which is great for finding places where you no longer need optional chaining, or where a condition is always true/false because you forgot what types things were.
That said, I tend to take a much more lean approach to linting these days. I try to only include lints that are actionable, likely to lead to real bugs, and have no false positives. Everything else feels like it's better handled in code review. If you don't know why your function is too complex and difficult to read, I don't want you to rewrite it just to satisfy a robot that's following arbitrary rules! I want to be able to have a discussion with you about proper techniques for splitting apart code.
But there's definitely people who go the other way entirely and build incredibly strict ESLint configurations, so looking for those might help you if that's what you want.
I wonder if anyone here has a good set of Typescript/ESlint addons to suggest? I also miss my duplicate code alert from Ruby!
Don't forget about Brakeman!
It’s been quite some time since I’ve used Rubocop but I did fight an uphill battle to get it into place. One of the things I had a difficult time explaining was that rules subtly interact with each other. When one rule makes a change, it might simplify something such that another rule can then also make a change. Once I enabled enough rules, there were some pretty impressive chains that were whittled down to almost nothing… but the same application of those rules elsewhere caused hemming and hawing by the naysayers.
I think the best thing that these tools give, above all else, is a way to settle petty opinions in a team. It’s why there is value in rules that either conflict with each other or are configurable so as to meet the expectations of a given team.
Yep. I introduced Rubocop at a past gig… only to watch my colleague write worse code playing whackamole trying to adhere to the rules.
I kept telling him that the whackamole is a sign to rework things at a higher level - that the Rubocop rule catches symptoms, and that he needed to address the cause, but…
PS - to be clear, I love Rubocop and swear by it. In that circumstance, I needed to introduce it differently, is all.
> is a way to settle petty opinions in a team.
Yeah this is huge. So often I get my PRs rejected because one dev has their own personal preference for formatting something. With Rubocop I can tell them to go submit a PR themselves to make that preference a rule and we can all discuss it and enforce it / autoformat it going forward. But for now, it won't be blocking my change going out.
Hopefully rubocop-rails-omakase [1] will pull in stuff like this. I've gone from custom Rubocop rules in various projects to just using rubocop-rails-omakase, which finally feels like a consensus of sorts. I think Standard [2] is harmfully redundant, personally.
[1] https://github.com/rails/rubocop-rails-omakase [2] https://github.com/standardrb/standard
I'm curious why you think Standard is "harmfully redundant"?
Rubocop is one of the worst Ruby tools there is. Flog and reek are also unhelpful, but Rubocop is style nonsense that doesn’t belong in a linter. The ESLint community used to be big on useless opinion based rule overload, until everyone realized how bad of an idea it was.
Can you be kore specific? I haven't used it for a few years but found it yo be a perfectly competent and helpful linter at minimum.
Nice. Now we can probably ask to move there half of the too nosy rubocop rules from the main rubocop repo. And then to never install rubocop-obsession!
Right? I seem to be the only high-level SWE at my company who hates RuboCop. Some of it's fine (e.g. no globals, use x.count { ... } instead of x.select { ... }.count). But so many of the rules are obsessed with trivial details like the "right" style of blocks to use (do vs {}) or "" vs '' for strings. Pointless busy work. I think the dumbest is requiring "def (x=true)" over "def (x = true)". Seriously, why could that possibly matter??
I see that as: it doesn't matter, therefore let's do one consistent thing and you'll never get a person saying "this should be x=true". RoboCop forces it one way and I never have to have a syntax/formatting discussion again.
You may think it really really doesn't matter, to the point that whatever form should be accepted. But then, would you accept my "def foo (x =true ,y= false )"? I've seen enough PRs with that kind of code to know that people will submit it one day.
> I think the dumbest is requiring "def (x=true)" over "def (x = true)". Seriously, why could that possibly matter??
For Python, I use ruff to autoreformat my code into a consistent style. The CI pipeline fails the PR if the style is violated it. Fixing it is a one-liner. Actually, if you use VSCode’s ruff extension, a zero-liner, since it auto-formats on save.
For Java, I use Diffplug Spotless for a similar experience.
One advantage of forcing the code to be auto-formatted, is it makes diffs cleaner - you eliminate diffs where someone just changed the code style, since the code style never changes.
`””` vs `''` isn’t just a style rule. In Ruby there are performance implications with double quoted strings. Strings in double quotes are interpolated for variable substitution, whereas strings in single quotes are processed literally, no variable replacement overhead. Is it going to matter for most code? Probably not, but good to know nonetheless imo.
This sounds intuitively true, but it’s a (very persistent) myth: https://www.viget.com/articles/just-use-double-quoted-ruby-s...
TIL. Thanks
Why does it matter when there is auto-fix-on-save? I just write whatever, hit ctrl+s and the code is fixed. The result: The code is "fixed" based on whatever rule and it doesn't cost me a thing. I don't think about it and I don't lift a finger.
But I do personally appreciate the tidiness of code-consistency.
That's my argument - if we collectively care, it should happen automatically. Alas, no one else seems bothered by the status quo.
Most of these trivial rubocop rules autoformat so it doesn't bother anyone. The ones that don't are usually odd things like code that doesn't run or things with the same name, which can't be safely autofixed.
Some of these rules do support autocorrect
I'm getting this right away!
The world runs on code that violates some or all of these rules. Following them won't fix any of the problems they have. It doesn't matter.
It does help at some margin to make code more readable. Humans just aren't that great at reading and understanding code. It has been said: "Write for humans, not computers because computers will understand what you wrote no matter how badly written". So if you're writing code on a team, do your co-workers and your future-self a favour and write code that's easy to read. It won't run any better or with fewer bugs, but it will be easier to understand later, easier to be understood by others, and "wishful thinking here": easier to debug.