-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce cyclomatic comeplxity threshold #61
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One nit, otherwise LGTM 😍
cli/command/container/opts.go
Outdated
@@ -12,7 +12,9 @@ import ( | |||
"time" | |||
|
|||
"github.com/Sirupsen/logrus" | |||
"github.com/docker/cli/cli/compose/loader" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hum although I like the fact we re-use this code, I would have extract it from there (because now container
package depends on the compose
package). But this is a nit 👼
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree , I was thinking about that too. Currently it outputs a compose type, so it would possibly require a larger change to create some shared type, and then translate it to a compose type later.
I think it's inevitable there is going to be sharing between compose and other cli components. I think we use service
functions in convert
right now
I've found gocyclo to not be very useful in the past. Personally, I don't like mechanically enforcing these limits. |
I find gocyclo to be an extremely value code metric. Most functions which have high cyclomatic complexity are not very readable and would benefit from refactoring into smaller functions. The only exception I've found to that rule are large For those rare cases where there is value in a longer function we can whitelist the function. |
Yes, the false positives with long I agree there may be high correlation between an unreadable function and one where gocyclo returns a high score, but if a function is unreadable, we shouldn't need a metric to realize that during the code review process. |
Yes I do, I think it's extremely valuable. No new PRs should really add to the whitelist. If a change puts a function over the threshold that's a good signal that the function needs to be refactored. Adding to the whitelist should only be allowed in very rare situations. Existing code is white listed for now, but these whitelist comments should really be treated as
Unfortunately that doesn't seem to be the case. We've had the same PR review policy for a long time, and yet we have many functions that are way too complex. I think one reason for that might be when you look at a PR you only really see a diff. The diff might only show you that you're adding a few lines, maybe 1 or 2 new conditions to a function. But over time 4 or 5 of those PRs results in an overloaded function. That's harder for a human to see. Readable is also very subjective. I'm sure most functions are readable if you study them for long enough. It's nice to have a more objective number. |
I agree that's a plausible explanation why complexity gets into the source code 👍 |
This was another problem I ran into with gocyclo in the past. Occasionally there would be a bug fix that needed a conditional to be added which put the function over the complexity threshold. Then a 3-line fix would become a big refactor, and it was much harder to have confidence in the fix and backport it. I'm still very much against mechanically enforcing this. I think it's fine to emit a list of potential refactoring candidates for human review. But failing CI because a function has too many conditionals is going too far. |
I think what you see as a problem I see as a benefit :)
If the complexity of that function wasn't already at the threshold maybe that bug would have been discovered earlier, because bugs are more noticeable in less complex code. So instead of requiring a critical fix after a release it would have already been fixed before the release!
If you aren't confident in a change it's a sign it doesn't have adequate testing. I don't think a few extra lines in a diff should significantly impact confidence in a change.
Warnings are not enough. It should be up to the contributor who is adding overly complex code to fix it, not up to someone else to come by later on and clean things up. We already fail CI for lots of reasons including test failures, gofmt/golint/vet failures. Why would gocyclo be any different? They all signal the same thing: problems with the code. There are sometimes going to be exceptional circumstances that require a test to be disabled temporarily, or a complex function to be whitelisted. That's fine, we have a mechanism to make those temporary exceptions. But the standard should be to enforce good quality contributions. |
I would urge extreme caution in enabling And, from what I can tell, cyclomatic complexity has largely been debunked as an indicator of defects:
Indeed, we do run several checks as part of CI, but we should be cautious in ensuring that such checks provide value and don't contribute to a higher defect rate. |
Codecov Report
@@ Coverage Diff @@
## master #61 +/- ##
==========================================
+ Coverage 46.14% 46.31% +0.16%
==========================================
Files 193 193
Lines 16073 16076 +3
==========================================
+ Hits 7417 7445 +28
+ Misses 8269 8241 -28
- Partials 387 390 +3 |
@stevvooe @aaronlehmann I see the discussion here is mainly about making the cyclomatic complexity check a requirement. Separate from that discussion, do the changes in this PR look good to you? |
@thaJeztah No, this PR is proposing changes to satisfy the constraints that are laid out by the cyclo check. Because the checks performed by gocyclo are roughly junk science, changes performed in pursuit satisfying gocyclo should be considered invalid. Specifically, in this case, this PR is actually exporting a function, |
These are changes I would have made anyway. Complex functions are code debt.
It was always our intent to have a single piece of code for parsing volumes from the client. This was just a good opportunity to accomplish that goal.
Any special cases that apply to the flag would also apply to the Compose file parsing. The logic in both cases should be identical, and it was a bug that they were not using the same code. Unfortunately the test cases for |
@dnephin How this PR proposes merging the
This is what has me concerned. The definition of a "valid volume spec" is what is supported by |
Can you point one out as example? A quick look and I'm not really finding anything right now. |
I can see why someone would make that assumption, but I don't believe that's the case. The official parsing of a volume spec is handled by the server, and depends on the platform. The daemon parses the volume spec. The The test cases included cases like '{ I understand your concern. This parsing is error prone and has lots of strange special cases. I'll see about restoring more of the previous test cases. Edit: other cases are things like mode |
@dnephin The issue here is it is not parsing a volume it's deciding how to apply the spec, e.g. is it a volume or is it a bind. |
Ok, I've found a much better way of preserving the test cases. Instead of trying to recreate the output of It appears that the term "bind" here is not exactly the same as other places. Any volume with a source was being added as a "bind". I've updated the caller to check I've restored the old test cases, and they pass without any modification to the test cases. |
It's only used to check the length of the array. It was effectively the same as returning |
This check doesn't seem quite right, either. The specs are passed in I guess, they would be of type "volume" or "bind" with a defined source. |
Yes, that sounds right, and the code should reflect that now |
I've moved the volume spec parsing change to #152 so that change can be discussed in isolation. That change is no longer part of this PR. |
I still think enforcing a more strict gocylo threshold is going in the wrong direction. I think we should remove the cyclomatic complexity check completely. |
Yes, I understand that. I still think that this check is valuable. 16 is not really "strict", it's actually well above what is considered an acceptable level of cyclomatic complexity (10). To summarize my arguments for enforcing this check:
It is true that cyclomatic complexity is not a perfect measure of complexity, but it is the best available proxy, and we have a mechanism for white listing exceptions. I'm not really seeing the downside to enforcing this lint check. I'm going to try to summarize some of the arguments to better understand your concerns.
I believe many of these concerns can be addressed by white listing a function. I think we should at the very least try this with a more reasonable limit (16). If we find that after a few months that the hassle of adding a comment to some functions outweights the value, we can always increase the limit, or even disable it. |
8b73299
to
c91b3ab
Compare
d3f89d7
to
f68d98e
Compare
9e701f6
to
f291f7a
Compare
5a57da9
to
afecf91
Compare
afecf91
to
70af574
Compare
Signed-off-by: Daniel Nephin <dnephin@docker.com>
Signed-off-by: Daniel Nephin <dnephin@docker.com>
Signed-off-by: Daniel Nephin <dnephin@docker.com>
Improve test coverage Reduce cyclo to 16 Signed-off-by: Daniel Nephin <dnephin@docker.com>
70af574
to
1475d7d
Compare
Reduced from 19 to 16.