4 ms·
> Ruby is one of the most dangerous dynamic languages to refactor A thousand times this. It's so much easier to do breaking changes and refactors in a language
by majewsky 8y ago
> Ruby is one of the most dangerous dynamic languages to refactor
A thousand times this. It's so much easier to do breaking changes and refactors in a language that's supporting you, instead of working against you.
- cknoxrun 8y agoDynamic language or not, having good test coverage will be your biggest support.
- weberc2 8y agoIt's definitely important, but we (Python shop) have "good" (85%) test coverage and we still see 500s in prod every day because of things a type checker could trivially catch. And this is just in the course of normal operation; this isn't even a migration. Having extensive experience with both Go and Python, I would conservatively estimate that Go requires ~30% fewer tests than untyped Python for the same confidence. That's more than 30% time savings; not only are you not writing 30% of the tests, but that's 30% fewer tests to have to maintain. Of course these aren't the only considerations--for certain tasks Python may be faster to develop with (although I think people forget about things like deployment, tooling, dependency management, performance requirements, etc when they make their estimations).
- jb3689 8y agoI noticed something similar with Elixir with regards to the number of tests we needed thanks to the compiler, but it also has late binding (albeit it is statically typed). A "best of both worlds" exists out there
- MattHeard 8y agoLine coverage is not test coverage. Having a test execute every line in your application doesn't mean your application is _covered_ or _tested_.
- weberc2 8y agoYes, we all know the metric is easily gamed but no one at our org is trying to game the metric. We are paid to build a product, not to boost the metric.
- kbp 8y agoIt's not about gaming the metric, it's just that the metric doesn't mean very much in the first place. Running a coverage tool during tests won't show you edge cases you forgot to handle in the code under test, it will show you code that's not tested at all. That can sometimes be useful for pointing out blind spots, but you shouldn't derive any confidence in the tests from a high coverage score, even if the people who worked on the project had the best intentions. Coverage tools could only measure quality of a test suite if you're assuming that either the code is perfect or that the existing tests cover (logically) everything about what they test. Without either of those guarantees, it doesn't tell you anything very meaningful, as you discovered.
- weberc2 8y agoThe metric is meaningful; I think you’re misinterpreting it. To your point, 100% coverage doesn’t mean you’ve eliminated all bugs, but it does mean that your code base almost certainly has a lower bug yield that the code base with 50% coverage (assuming no one has games the metric). If you really think that the metric is meaningless and useless for deriving confidence, then you are necessarily asserting that code bases with 100% coverage have indistinguishable bug yields compared to those with 50%, 5%, or even 0% coverage. A claim like this is too extraordinary to be believed without considerable evidence.
- kbp 8y agoI guess it's useful for deriving a baseline level of confidence, like a low coverage score is a red flag, and an increasing coverage score probably corresponds to increasing test coverage, but my issue is that 100% coverage doesn't mean anything about the correctness of the code in absolute terms (unless 'gaming the metric' includes not thinking of every edge case, ie, that we're assuming the existing test suite is perfect). If you're working on a poorly tested codebase, it's a useful relative metric of your progress in testing what already exists, but unless you're assuming the code is already correct, that doesn't mean anything more than that. If you wanted to derive, say, the confidence that you won't see 500's daily in production from a metric, then line coverage isn't an effective one to use for that; the tests you write that give you that kind of confidence don't really help your coverage score. The parts of the codebase that are in the most urgent need of tests for getting that kind of confidence in places will most of the time be ones that already have good coverage; think of how TDD works, even if you're not doing TDD. I could agree that a high coverage score is a prerequisite for having confidence that your test suite is comprehensive (in general), but that's such a low bar, it's like saying a full bath is a prerequisite for a nice house, just knowing that shouldn't do much to convince you it's a mansion.
- ezekg 8y agoIntegration tests are your friend. ;)
- thefounder 8y agoWhat dynamic language is easy to refactor? I remember the "good" old PHP days and I can't say it was easy either.
- coder543 8y agoTypeScript and Python 3 support gradual typing, so code can be optionally annotated with types. This makes refactoring easier, while still benefitting from how you can quickly sketch out new code dynamically and only annotate it once things start to solidify. Personally, I'm a fan of very strong, static type systems, so I would prefer to annotate all the things, but I understand other people have different views.
- recursive 8y agoTypeScript probably wouldn't call itself a dynamic language.
- coder543 8y agoFrom what I understand, everywhere that you would need to write a type, TypeScript allows you to either not declare a type at all, or put "Any" and avoid declaring a specific type. I think you can even go so far as to rename a ".js" file as ".ts", and it's fine, which is a great example of "gradual typing", and I think this all makes TypeScript a dynamic language.
- zamalek 8y agoIt's statically typed with opt-in dynamic (which is opt-out implicit). Sans the implicit opt-out, this is identical to C#.
- coder543 8y agoIf you don't specify types at all, is that really opting out? This is valid TypeScript code: function foo(a, b) { if (a != 3) { return a + b } else { return "hello" } } console.log(foo(2, 5)); //prints 7 console.log(foo(3, 5)); //prints "hello" You can compile it and run it on the typescript playground: https://www.typescriptlang.org/play/ https://www.typescriptlang.org/play/ This is not like C#, where would be required to specify that every type is "dynamic". Here, you can optionally choose to specify that the types are "Any", but it's still gradual typing. EDIT: you said "implicit opt-out"... which sounds like a synonym for "opt-in". I don't think "implicit opt-out" is a term.
- jb3689 8y agoI disagree with this slightly. I've been using Ruby for a long, long time and Ruby does give you the tools to refactor safely - few people actually know how to use them though and there aren't very many high-level tools aimed at doing safe large-scale refactoring. In Ruby you can trace calls and do very powerful introspection which you can verify to make sure things like delegation happened properly It also doesn't help when most codebases are using ActiveRecord or something in every complex class and wind up increasing the interface width and ancestor depth of their code significantly. The point is - I think the language does a pretty good job supporting the developers, but there are a lot of bad practices that are still in use and recommended. Can't fault the language because people are writing shit code
- sorbits 8y agoWhat tools are you talking about? The “problem” with ruby is that you basically don’t know if code is valid unless you execute it, whereas a statically typed language will enforce a lot of things during compilation and simply not allow the program to compile, if API is used in the wrong way, this could be calling private functions, referencing undefined symbols (simple typos), wrong number of arguments, passing a string where integer is expected, etc. Additionally access control is limited in Ruby, which makes it difficult to release a library and have the language enforce that people do not rely on things which are implementation details subject to change.
- jb3689 8y agoYou're right - all of those things are possible. What I'm arguing is that given you have a reasonably good test suite and are aware of what patterns to are dangerous, you can refactor fairly confidently with the tools given For example, it's possible to fetch a class's entire interface before and after a refactor and validate it is the same. It's possible to dynamically wrap every method you're refactoring track them and type check them. And if you extend that idea, now you can output this data to files and perform static analysis. Sure, it all relies on you having some safe execution context to get this information but Ruby probably has the best testing tools of any language and many projects have great test coverage For library owners, I agree with you - there's no hope. But for application developers maintaining their monoliths with nothing depending on them there's a lot you can do to ensure safe refactoring