AWS DevOps & Developer Productivity Blog
Finding code inconsistencies using Amazon CodeGuru Reviewer
Here we are introducing the inconsistency detector for Java in Amazon CodeGuru Reviewer. CodeGuru Reviewer automatically analyzes pull requests (created in supported repositories such as AWS CodeCommit, GitHub, GitHub Enterprise, and Bitbucket) and generates recommendations for improving code quality.
The Inconsistency Principle
Software is repetitive, so it’s possible to mine usage specifications from the mining of large code bases. While the mined specifications are often correct, specification violations are not often bugs. This is because the code uses are contextual, therefore contextual analysis is required to detect genuine violations. Moreover, naive enforcement of learned specifications leads to many false positives. The inconsistency principle is based on the following observation: While deviations from frequent behavior (or specifications) are often permissible across repositories or packages, deviations from frequent behavior inside of a given repository are worthy of investigation by developers, and these are often bugs or code smells.
Consider the following example. Assume that within a repository or package, we observe 10 occurrences of this statement:
And assume that there is one occurrence of the following statement in the same package:
The inconsistency can be described as: in this package, typically there is an API call of toLowerCase()
follows AppConfig.getDomain()
that converts the string generated by AppConfig.getDomain()
to its lower-case correspondence. However, in one occurrence, the call is missing. This should be examined.
In summary, the inconsistency detector can learn from a single package (read: your own codebase) and can identify common code usage patterns in the code, such as common patterns of logging, throwing and handling exceptions, performing null checks, etc. Next, usage patterns inconsistent with frequent usage patterns are flagged. While the flagged violations are most often bugs or code smells, it is possible that in some cases the majority usage patterns are the anti-patterns and the flagged violations are actually correct. Mining and detection are conducted on-the-fly during code analysis, and neither the code nor the patterns are persisted. This ensures that the customer code privacy requirements are preserved.
Diverse Recommendations
Perhaps the most distinguishing feature of the inconsistency detector family is how it is based on learning from the customer’s own code while conducting analysis on-the-fly without persisting the code. This is rather different than the pre-defined rules or dedicated ML algorithms targeting specific code defects like concurrency. These approaches also persist learned patterns or models. Therefore, the inconsistency principle-based detectors are much more diversified and spread out to a wide range of code problems.
Our approach aims to automatically detect inconsistent code pieces that are abnormal and deviant from common code patterns in the same package—assuming that the common behaviors are correct. This anomaly detection approach doesn’t require prior knowledge and minimizes the human effort to predefine rule sets, as it can automatically and implicitly “infer” rules, i.e., common code patterns, from the source code. While inconsistencies can be detected in the specific aspects of programs (such as synchronization, exceptions, logging, etc.), CodeGuru Reviewer can detect inconsistencies in multiple program aspects. The detections aren’t restricted to specific code defect types, but instead cover diverse code issue categories. These code defects may not necessarily be bugs, but they could also be code smells that should be avoided for better code maintenance and readability. CodeGuru Reviewer can detect 12 types of code inconsistencies in Java code. These detectors identify issues like typos, inconsistent messaging, inconsistent logging, inconsistent declarations, general missing API calls, missing null checks, missing preconditions, missing exceptions, etc. Now let’s look at several real code examples (they may not cover all types of findings).
Typo
Typos frequently appear in code messages, logs, paths, variable names, and other strings. Even simple typos can be difficult to find and can cause severe issues in your code. In a real code example below, the developer is setting a configuration file location.
The inconsistency detector identifies a typo in the path amazom
→ amazon
. Failure to identify and fix this typo will make the configuration location not found. Accordingly, the developer fixed the typo. In some other cases, certain typos are customized to the customer code and may be difficult to find via pre-defined rules or vocabularies. In the example below, the developer sets a bean name as a string.
The inconsistency detector learns from the entire codebase and identifies that um
should instead be uam
, as uam
appears in multiple similar cases in the code, thereby making the use of um
abnormal. The developer responded “Cool!” to this finding and accordingly made the fix.
Inconsistent Logging
In the example below, the developer is logging an exception related to a missing field, ID_FIELD
.
The inconsistency detector learns the patterns in the developer’s code and identifies that, in 47 other similar cases of the same codebase, the developer’s code uses a log level of error
instead of info
, as shown below.
Utilizing the appropriate log level can improve the application debugging process and ensure that developers are aware of all potential issues within their applications. The message from the CodeGuru Reviewer was “The detector found 47 occurrences of code with logging similar to the selected lines. However, the logging level in the selected lines is not consistent with the other occurrences. We recommend you check if this is intentional. The following are some examples in your code of the expected level of logging in similar occurrences: log.error (“Missing {} from data {}”, ID_FIELD, data); …”
The developer acknowledges the recommendation to make the log level consistent, and they made the corresponding change to upgrade the level from info
to error
.
Apart from log levels, the inconsistency detector also identifies potentially missing log statements in your code, and it ensures that the log messages are consistent across the entire codebase.
Inconsistent Declaration
The inconsistency detector also identifies inconsistencies in variable or method declarations. For instance, a typical example is missing a final
keyword for certain variables or functions. While a variable doesn’t always need to be declared final
, the inconsistency determines certain variables as final
by inferring from other appearances of the variables. In the example below, the developer declares a method with the variable request
.
Without prior knowledge, the inconsistency learns from 9 other similar occurrences of the same codebase, and it determines that the request
should be with final
keyword. The developer responded by saying “Good catch!”, and then made the change.
Missing API
Missing API calls is a typical code bug, and their identification is highly valued by customers. The inconsistency detector can find these issues via the inconsistency principle. In the example below, the developer deletes stale entities in the document object.
The inconsistency detector learns from 4 other similar methods in the developer’s codebase, and then determines that the API document.deleteEntity()
is strongly associated with another API document.acceptChanges()
, which is usually called after document.deleteEntity()
in order to ensure that the document can accept other changes, while it is missing in this case. Therefore, the inconsistency detector recommends adding the document.acceptChanges()
call. Another human reviewer agrees with CodeGuru’s suggestion, and they also refer to the coding guidelines of their platform that echo with CodeGuru’s recommendation. The developer made the corresponding fix as shown below.
Null Check
Consider the following code snippet:
CodeGuru Reviewer indicates that the output generated by the System.getProperty(MB_SERVER_OVERRIDE_PROPERTY)
misses a null-check. A null-check appears to be available in the next line. However, if we look carefully, that null check is not applied to the correct output. It is a copy-paste error, and instead of checking on ServerOverride
it checks ClientOverride
. The developer fixed the code as the recommendation suggested.
Exception Handling
The following example shows an inconsistency finding regarding exception handling.
Detection:
Supporting Code Block:
There are 4 occurrences of handling an exception thrown by Double.parseDouble
using catch with InvalidSpotFleetRequestConfigException()
. The detected code snippet with an incredibly similar context simply uses InvalidParameterException()
, which does not provide as much detail as the customized exception. The developer responded with “yup the detection sounds right”, and then made the revision.
Responses to the Inconsistency Detector Findings
Large repos are typically developed by more than one developer over a period of time. Therefore, maintaining consistency for the code snippets that serve the same or similar purpose, code style, etc., can easily be overlooked, especially for repos with months or years of development history. As a result, the inconsistency detector in CodeGuru Reviewer has flagged numerous findings and alerted developers within Amazon regarding hundreds of inconsistency issues before they hit production.
The inconsistency detections have witnessed a high developer acceptance rate, and developer feedback of the inconsistency detector has been very positive. Some feedback from the developers includes “will fix. Didn’t realize as this was just copied from somewhere else.”, “Oops!, I’ll correct it and use consistent message format.”, “Interesting. Now it’s learning from our own code. AI is trying to take our jobs haha”, and “lol yeah, this was pretty surprising”. Developers have also concurred that the findings are essential and must be fixed.
Conclusion
Inconsistency bugs are difficult to detect or replicate during testing. They can impact production services availability. As a result, it’s important to automatically detect these bugs early in the software development lifecycle, such as during pull requests or code scans. The CodeGuru Reviewer inconsistency detector combines static code analysis algorithms with ML and data mining in order to surface only the high confidence deviations. It has a high developer acceptance rate and has alerted developers within Amazon to hundreds of inconsistencies before they reach production.