Codacy for Code Quality in Java: Automated Code Review and Quality Gates

Codacy is an automated code review tool that helps Java development teams maintain code quality by identifying issues, enforcing standards, and providing quality metrics. It integrates with GitHub, GitLab, and Bitbucket to provide continuous code quality monitoring across your Java projects.

Understanding Codacy Architecture

Key Components:

  • Static Analysis for multiple languages including Java
  • Code Coverage integration
  • Duplicate Code detection
  • Complexity Analysis (cyclomatic complexity)
  • Security Vulnerability scanning
  • Quality Gates for PR validation

Core Implementation Patterns

1. Project Setup and Codacy Configuration

Configure Codacy for Java projects with comprehensive quality checks.

codacy.yml Configuration:

# Codacy configuration file
exclude_paths:
- "**/generated/**"
- "**/test/**"
- "**/target/**"
- "**/build/**"
- "**/*.md"
engines:
# Java-specific tools
checkstyle:
enabled: true
exclude_paths:
- "**/test/**"
pmd:
enabled: true
exclude_paths:
- "**/test/**"
findbugs:
enabled: true
jshint:
enabled: false  # Disable for Java projects
# Multi-language tools
eslint:
enabled: false
duplication:
enabled: true
config:
languages:
- java
metrics:
enabled: true
# Quality gate configuration
quality:
# Code quality requirements
code_patterns:
# Security issues - must be zero
must_fix:
- "SQL_INJECTION_JAVA"
- "COMMAND_INJECTION"
- "PATH_TRAVERSAL_IN"
- "WEAK_CRYPTO_ALGO"
# Code quality issues - threshold based
code_quality:
- pattern: "HIGH_COMPLEXITY"
threshold: 5
- pattern: "DUPLICATED_CODE"
threshold: 3%
- pattern: "LONG_METHOD"
threshold: 10
- pattern: "TOO_MANY_PARAMETERS"
threshold: 5
# Coverage requirements
coverage:
min_coverage: 80
max_decrease: 5
# Security requirements
security:
max_issues: 0
categories:
- "Security"
- "Injection"
# Documentation requirements
documentation:
min_comments_density: 20

.codacy.json Configuration:

{
"files": [
"src/main/java/**/*.java",
"src/test/java/**/*.java"
],
"exclude_paths": [
"target/**",
"build/**",
"**/generated/**",
"**/test/resources/**"
],
"tools": {
"checkstyle": {
"enabled": true,
"config_file": ".checkstyle.xml"
},
"pmd": {
"enabled": true,
"config_file": "rulesets/java/quickstart.xml"
},
"spotbugs": {
"enabled": true,
"config_file": ".spotbugs.xml"
},
"duplication": {
"enabled": true,
"config": {
"languages": ["java"]
}
}
},
"quality_gates": [
{
"name": "Security Gate",
"conditions": [
{
"tool": "spotbugs",
"pattern": ".*SECURITY.*",
"threshold": 0
}
]
},
{
"name": "Code Quality Gate",
"conditions": [
{
"metric": "duplication",
"threshold": 5,
"operator": "less_than"
},
{
"metric": "complexity",
"threshold": 3.0,
"operator": "less_than"
}
]
}
]
}

2. Code Quality Domain Models

Create models for Codacy analysis results and quality metrics.

Quality Models:

@Data
public class CodacyAnalysisResult {
private String commitId;
private String branch;
private AnalysisSummary summary;
private List<CodeIssue> issues;
private List<DuplicateCode> duplicates;
private CodeMetrics metrics;
private List<QualityGateResult> qualityGates;
private Date analysisDate;
public boolean isQualityGatePassed() {
return qualityGates.stream().allMatch(QualityGateResult::isPassed);
}
public List<CodeIssue> getCriticalIssues() {
return issues.stream()
.filter(issue -> issue.getSeverity() == IssueSeverity.CRITICAL)
.collect(Collectors.toList());
}
public List<CodeIssue> getSecurityIssues() {
return issues.stream()
.filter(issue -> issue.getCategory() == IssueCategory.SECURITY)
.collect(Collectors.toList());
}
}
@Data
public class AnalysisSummary {
private int totalIssues;
private int criticalIssues;
private int majorIssues;
private int minorIssues;
private int infoIssues;
private double codeCoverage;
private double duplicationPercentage;
private double complexityAverage;
public boolean hasCriticalIssues() {
return criticalIssues > 0;
}
public double getQualityScore() {
double score = 100.0;
score -= criticalIssues * 10;
score -= majorIssues * 5;
score -= minorIssues * 2;
score -= Math.max(0, 80 - codeCoverage) * 0.5;
score -= duplicationPercentage * 2;
return Math.max(0, score);
}
}
@Data
public class CodeIssue {
private String id;
private String file;
private int line;
private String message;
private IssueSeverity severity;
private IssueCategory category;
private String patternId;
private String tool;
private Date firstSeen;
private boolean newIssue;
public String getFormattedLocation() {
return String.format("%s:%d", file, line);
}
}
public enum IssueSeverity {
CRITICAL, MAJOR, MINOR, INFO
}
public enum IssueCategory {
SECURITY, PERFORMANCE, MAINTAINABILITY, RELIABILITY, COMPATIBILITY, 
CODE_STYLE, DOCUMENTATION, DUPLICATION, COMPLEXITY
}
@Data
public class DuplicateCode {
private List<CodeLocation> locations;
private int lines;
private String fingerprint;
public boolean isCrossFile() {
return locations.stream()
.map(CodeLocation::getFile)
.distinct()
.count() > 1;
}
}
@Data
public class CodeLocation {
private String file;
private int startLine;
private int endLine;
public String getRange() {
return String.format("%d-%d", startLine, endLine);
}
}
@Data
public class CodeMetrics {
private int totalLines;
private int codeLines;
private int commentLines;
private int blankLines;
private int methods;
private int classes;
private int files;
private double commentDensity;
private double complexityAverage;
private double complexityMax;
public double getCommentDensityPercentage() {
return commentDensity * 100;
}
}
@Data
public class QualityGateResult {
private String name;
private boolean passed;
private String description;
private List<QualityCondition> failedConditions;
public boolean isPassed() {
return passed && (failedConditions == null || failedConditions.isEmpty());
}
}
@Data
public class QualityCondition {
private String metric;
private String operator;
private double threshold;
private double actualValue;
private String tool;
private String pattern;
}

3. Codacy Integration Service

Implement service for interacting with Codacy API and processing results.

Codacy Integration Service:

@Service
@Slf4j
public class CodacyIntegrationService {
private final CodacyApiClient apiClient;
private final CodacyConfig config;
private final ObjectMapper objectMapper;
public CodacyIntegrationService(CodacyConfig config) {
this.config = config;
this.apiClient = new CodacyApiClient(config.getApiToken());
this.objectMapper = new ObjectMapper();
}
/**
* Trigger Codacy analysis for a specific commit
*/
public AnalysisResult triggerAnalysis(String commitHash, String branch) {
try {
log.info("Triggering Codacy analysis for commit: {} on branch: {}", commitHash, branch);
AnalysisRequest request = new AnalysisRequest(commitHash, branch);
AnalysisResponse response = apiClient.triggerAnalysis(request);
// Wait for analysis to complete
AnalysisResult result = waitForAnalysisCompletion(response.getAnalysisId());
log.info("Codacy analysis completed for commit: {}", commitHash);
return result;
} catch (Exception e) {
log.error("Failed to trigger Codacy analysis for commit: {}", commitHash, e);
throw new CodacyIntegrationException("Analysis trigger failed", e);
}
}
/**
* Get analysis results for a commit
*/
public CodacyAnalysisResult getAnalysisResults(String commitHash) {
try {
AnalysisResult result = apiClient.getAnalysisResults(commitHash);
return mapToDomainModel(result);
} catch (Exception e) {
log.error("Failed to get analysis results for commit: {}", commitHash, e);
throw new CodacyIntegrationException("Failed to fetch analysis results", e);
}
}
/**
* Check if commit passes quality gates
*/
public QualityGateStatus checkQualityGate(String commitHash) {
try {
CodacyAnalysisResult analysis = getAnalysisResults(commitHash);
return evaluateQualityGates(analysis);
} catch (Exception e) {
log.error("Failed to check quality gate for commit: {}", commitHash, e);
return QualityGateStatus.ERROR;
}
}
/**
* Get code coverage data
*/
public CoverageData getCoverageData(String commitHash) {
try {
return apiClient.getCoverageData(commitHash);
} catch (Exception e) {
log.error("Failed to get coverage data for commit: {}", commitHash, e);
throw new CodacyIntegrationException("Failed to fetch coverage data", e);
}
}
/**
* Get historical quality trends
*/
public QualityTrends getQualityTrends(String branch, int days) {
try {
return apiClient.getQualityTrends(branch, days);
} catch (Exception e) {
log.error("Failed to get quality trends for branch: {}", branch, e);
throw new CodacyIntegrationException("Failed to fetch quality trends", e);
}
}
/**
* Upload custom coverage data to Codacy
*/
public void uploadCoverageData(String commitHash, CoverageReport coverageReport) {
try {
apiClient.uploadCoverageData(commitHash, coverageReport);
log.info("Uploaded coverage data for commit: {}", commitHash);
} catch (Exception e) {
log.error("Failed to upload coverage data for commit: {}", commitHash, e);
throw new CodacyIntegrationException("Coverage data upload failed", e);
}
}
private AnalysisResult waitForAnalysisCompletion(String analysisId) {
int maxAttempts = 30; // 5 minutes with 10-second intervals
int attempt = 0;
while (attempt < maxAttempts) {
try {
AnalysisStatus status = apiClient.getAnalysisStatus(analysisId);
if (status == AnalysisStatus.COMPLETED) {
return apiClient.getAnalysisResult(analysisId);
} else if (status == AnalysisStatus.FAILED) {
throw new CodacyIntegrationException("Analysis failed for: " + analysisId);
}
// Wait before next check
Thread.sleep(10000); // 10 seconds
attempt++;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CodacyIntegrationException("Analysis wait interrupted", e);
}
}
throw new CodacyIntegrationException("Analysis timeout for: " + analysisId);
}
private CodacyAnalysisResult mapToDomainModel(AnalysisResult result) {
CodacyAnalysisResult domainResult = new CodacyAnalysisResult();
domainResult.setCommitId(result.getCommit());
domainResult.setBranch(result.getBranch());
domainResult.setAnalysisDate(result.getCompletedAt());
// Map issues
domainResult.setIssues(result.getIssues().stream()
.map(this::mapIssue)
.collect(Collectors.toList()));
// Map duplicates
domainResult.setDuplicates(result.getDuplicates().stream()
.map(this::mapDuplicate)
.collect(Collectors.toList()));
// Map metrics
domainResult.setMetrics(mapMetrics(result.getMetrics()));
// Map quality gates
domainResult.setQualityGates(evaluateQualityGates(domainResult).getGateResults());
return domainResult;
}
private CodeIssue mapIssue(Issue issue) {
CodeIssue codeIssue = new CodeIssue();
codeIssue.setId(issue.getId());
codeIssue.setFile(issue.getFile());
codeIssue.setLine(issue.getLine());
codeIssue.setMessage(issue.getMessage());
codeIssue.setSeverity(mapSeverity(issue.getSeverity()));
codeIssue.setCategory(mapCategory(issue.getCategory()));
codeIssue.setPatternId(issue.getPatternId());
codeIssue.setTool(issue.getTool());
return codeIssue;
}
private QualityGateStatus evaluateQualityGates(CodacyAnalysisResult analysis) {
List<QualityGateResult> gateResults = new ArrayList<>();
boolean overallPassed = true;
// Security gate - no critical security issues
QualityGateResult securityGate = evaluateSecurityGate(analysis);
gateResults.add(securityGate);
if (!securityGate.isPassed()) overallPassed = false;
// Coverage gate - minimum coverage
QualityGateResult coverageGate = evaluateCoverageGate(analysis);
gateResults.add(coverageGate);
if (!coverageGate.isPassed()) overallPassed = false;
// Complexity gate - maximum complexity
QualityGateResult complexityGate = evaluateComplexityGate(analysis);
gateResults.add(complexityGate);
if (!complexityGate.isPassed()) overallPassed = false;
// Duplication gate - maximum duplication
QualityGateResult duplicationGate = evaluateDuplicationGate(analysis);
gateResults.add(duplicationGate);
if (!duplicationGate.isPassed()) overallPassed = false;
return new QualityGateStatus(overallPassed, gateResults);
}
private QualityGateResult evaluateSecurityGate(CodacyAnalysisResult analysis) {
List<CodeIssue> securityIssues = analysis.getSecurityIssues();
boolean passed = securityIssues.isEmpty();
QualityGateResult result = new QualityGateResult();
result.setName("Security Gate");
result.setPassed(passed);
result.setDescription("No security vulnerabilities allowed");
if (!passed) {
result.setFailedConditions(List.of(
new QualityCondition("security_issues", "equals", 0, securityIssues.size(), 
"codacy", "SECURITY")
));
}
return result;
}
// Other gate evaluation methods...
}
@Data
public class QualityGateStatus {
private final boolean passed;
private final List<QualityGateResult> gateResults;
private final String message;
public QualityGateStatus(boolean passed, List<QualityGateResult> gateResults) {
this.passed = passed;
this.gateResults = gateResults;
this.message = passed ? "All quality gates passed" : "Some quality gates failed";
}
}

4. Quality Analysis and Reporting

Implement comprehensive quality analysis and reporting.

Quality Analysis Service:

@Service
@Slf4j
public class QualityAnalysisService {
private final CodacyIntegrationService codacyService;
private final IssueTrackingService issueTracker;
private final NotificationService notificationService;
public QualityAnalysisService(CodacyIntegrationService codacyService,
IssueTrackingService issueTracker,
NotificationService notificationService) {
this.codacyService = codacyService;
this.issueTracker = issueTracker;
this.notificationService = notificationService;
}
/**
* Perform comprehensive quality analysis
*/
public QualityReport performQualityAnalysis(String commitHash, String branch) {
log.info("Starting quality analysis for commit: {}", commitHash);
CodacyAnalysisResult codacyResult = codacyService.getAnalysisResults(commitHash);
QualityGateStatus gateStatus = codacyService.checkQualityGate(commitHash);
CoverageData coverageData = codacyService.getCoverageData(commitHash);
QualityReport report = new QualityReport();
report.setCommitHash(commitHash);
report.setBranch(branch);
report.setAnalysisDate(new Date());
report.setCodacyResult(codacyResult);
report.setQualityGateStatus(gateStatus);
report.setCoverageData(coverageData);
report.setQualityScore(calculateQualityScore(codacyResult, coverageData));
// Track new issues
trackNewIssues(codacyResult);
// Send notifications if quality gates failed
if (!gateStatus.isPassed()) {
notificationService.sendQualityGateFailure(report);
}
log.info("Quality analysis completed for commit: {}", commitHash);
return report;
}
/**
* Analyze code quality trends
*/
public QualityTrendsAnalysis analyzeQualityTrends(String branch, int days) {
QualityTrends trends = codacyService.getQualityTrends(branch, days);
QualityTrendsAnalysis analysis = new QualityTrendsAnalysis();
analysis.setBranch(branch);
analysis.setAnalysisPeriod(days);
analysis.setTrends(trends);
analysis.setImprovementAreas(identifyImprovementAreas(trends));
analysis.setRecommendations(generateRecommendations(trends));
return analysis;
}
/**
* Compare quality between two commits
*/
public QualityComparison compareQuality(String baseCommit, String targetCommit) {
CodacyAnalysisResult baseResult = codacyService.getAnalysisResults(baseCommit);
CodacyAnalysisResult targetResult = codacyService.getAnalysisResults(targetCommit);
QualityComparison comparison = new QualityComparison();
comparison.setBaseCommit(baseCommit);
comparison.setTargetCommit(targetCommit);
comparison.setComparisonDate(new Date());
// Compare issues
comparison.setNewIssues(findNewIssues(baseResult, targetResult));
comparison.setFixedIssues(findFixedIssues(baseResult, targetResult));
comparison.setIssueDelta(calculateIssueDelta(baseResult, targetResult));
// Compare metrics
comparison.setCoverageChange(
targetResult.getMetrics().getCommentDensity() - 
baseResult.getMetrics().getCommentDensity());
comparison.setComplexityChange(
targetResult.getMetrics().getComplexityAverage() - 
baseResult.getMetrics().getComplexityAverage());
return comparison;
}
/**
* Generate quality improvement plan
*/
public ImprovementPlan generateImprovementPlan(String commitHash) {
CodacyAnalysisResult result = codacyService.getAnalysisResults(commitHash);
ImprovementPlan plan = new ImprovementPlan();
plan.setTargetCommit(commitHash);
plan.setGeneratedDate(new Date());
plan.setPriorityAreas(identifyPriorityAreas(result));
plan.setActionItems(generateActionItems(result));
plan.setEstimatedEffort(estimateEffort(plan.getActionItems()));
return plan;
}
private double calculateQualityScore(CodacyAnalysisResult codacyResult, CoverageData coverage) {
double score = 100.0;
// Deduct for issues
score -= codacyResult.getSummary().getCriticalIssues() * 10;
score -= codacyResult.getSummary().getMajorIssues() * 5;
score -= codacyResult.getSummary().getMinorIssues() * 2;
// Deduct for low coverage
if (coverage.getTotalCoverage() < 80) {
score -= (80 - coverage.getTotalCoverage()) * 0.5;
}
// Deduct for high duplication
if (codacyResult.getSummary().getDuplicationPercentage() > 5) {
score -= (codacyResult.getSummary().getDuplicationPercentage() - 5) * 2;
}
// Deduct for high complexity
if (codacyResult.getSummary().getComplexityAverage() > 3) {
score -= (codacyResult.getSummary().getComplexityAverage() - 3) * 5;
}
return Math.max(0, Math.min(100, score));
}
private void trackNewIssues(CodacyAnalysisResult result) {
result.getIssues().stream()
.filter(CodeIssue::isNewIssue)
.forEach(issue -> {
issueTracker.trackNewIssue(issue);
notificationService.sendNewIssueNotification(issue);
});
}
private List<ImprovementArea> identifyPriorityAreas(CodacyAnalysisResult result) {
List<ImprovementArea> areas = new ArrayList<>();
// Security issues are highest priority
if (!result.getSecurityIssues().isEmpty()) {
areas.add(new ImprovementArea("Security", "CRITICAL", 
result.getSecurityIssues().size()));
}
// High complexity methods
if (result.getMetrics().getComplexityMax() > 10) {
areas.add(new ImprovementArea("Complexity", "HIGH", 
(int) result.getMetrics().getComplexityMax()));
}
// Duplication
if (result.getSummary().getDuplicationPercentage() > 5) {
areas.add(new ImprovementArea("Duplication", "MEDIUM", 
(int) result.getSummary().getDuplicationPercentage()));
}
// Low coverage
if (result.getSummary().getCodeCoverage() < 80) {
areas.add(new ImprovementArea("Test Coverage", "MEDIUM", 
(int) (100 - result.getSummary().getCodeCoverage())));
}
return areas;
}
// Additional helper methods for analysis...
}
@Data
public class QualityReport {
private String commitHash;
private String branch;
private Date analysisDate;
private CodacyAnalysisResult codacyResult;
private QualityGateStatus qualityGateStatus;
private CoverageData coverageData;
private double qualityScore;
private String grade;
public String getGrade() {
if (qualityScore >= 90) return "A";
if (qualityScore >= 80) return "B";
if (qualityScore >= 70) return "C";
if (qualityScore >= 60) return "D";
return "F";
}
}
@Data
public class ImprovementPlan {
private String targetCommit;
private Date generatedDate;
private List<ImprovementArea> priorityAreas;
private List<ActionItem> actionItems;
private String estimatedEffort;
private Date targetCompletion;
public boolean isCritical() {
return priorityAreas.stream()
.anyMatch(area -> "CRITICAL".equals(area.getPriority()));
}
}
@Data
public class ImprovementArea {
private String category;
private String priority; // CRITICAL, HIGH, MEDIUM, LOW
private int issueCount;
private String description;
public ImprovementArea(String category, String priority, int issueCount) {
this.category = category;
this.priority = priority;
this.issueCount = issueCount;
this.description = generateDescription(category, issueCount);
}
private String generateDescription(String category, int count) {
switch (category) {
case "Security":
return String.format("%d security vulnerability(s) need immediate attention", count);
case "Complexity":
return String.format("Code complexity exceeds thresholds in %d area(s)", count);
case "Duplication":
return String.format("%d%% code duplication detected", count);
case "Test Coverage":
return String.format("Test coverage is %d%% below target", count);
default:
return String.format("%d issues in %s category", count, category);
}
}
}

5. GitHub/GitLab Integration

Implement integration with version control systems.

GitHub Integration Service:

@Service
@Slf4j
public class GitHubIntegrationService {
private final GitHubClient githubClient;
private final CodacyIntegrationService codacyService;
private final QualityAnalysisService qualityService;
public GitHubIntegrationService(GitHubConfig config,
CodacyIntegrationService codacyService,
QualityAnalysisService qualityService) {
this.githubClient = new GitHubClient(config);
this.codacyService = codacyService;
this.qualityService = qualityService;
}
/**
* Process pull request and add quality status
*/
public void processPullRequest(PullRequestEvent event) {
try {
String commitHash = event.getHeadCommit();
String branch = event.getBranch();
// Trigger Codacy analysis
codacyService.triggerAnalysis(commitHash, branch);
// Wait for analysis and get results
CodacyAnalysisResult analysis = codacyService.getAnalysisResults(commitHash);
QualityGateStatus gateStatus = codacyService.checkQualityGate(commitHash);
// Create status check on GitHub
createStatusCheck(event, gateStatus, analysis);
// Add comment with quality report
addQualityComment(event, analysis);
log.info("Processed PR {} for commit {}", event.getPrNumber(), commitHash);
} catch (Exception e) {
log.error("Failed to process pull request: {}", event.getPrNumber(), e);
createErrorStatus(event, e.getMessage());
}
}
/**
* Create GitHub status check
*/
private void createStatusCheck(PullRequestEvent event, QualityGateStatus gateStatus, 
CodacyAnalysisResult analysis) {
StatusCheck status = new StatusCheck();
status.setContext("codacy/quality");
status.setDescription(generateStatusDescription(gateStatus, analysis));
status.setState(mapToGitHubState(gateStatus));
if (!gateStatus.isPassed()) {
status.setTargetUrl(generateDetailsUrl(event, analysis));
}
githubClient.createStatus(event.getRepository(), event.getHeadCommit(), status);
}
/**
* Add quality report comment to PR
*/
private void addQualityComment(PullRequestEvent event, CodacyAnalysisResult analysis) {
String comment = generateQualityComment(analysis);
githubClient.addComment(event.getRepository(), event.getPrNumber(), comment);
}
/**
* Generate quality status comment
*/
private String generateQualityComment(CodacyAnalysisResult analysis) {
StringBuilder comment = new StringBuilder();
comment.append("## 📊 Codacy Quality Report\n\n");
// Summary
comment.append("### Quality Summary\n");
comment.append(String.format("- **Quality Score**: %.1f/100\n", 
analysis.getSummary().getQualityScore()));
comment.append(String.format("- **Issues Found**: %d\n", 
analysis.getSummary().getTotalIssues()));
comment.append(String.format("- **Code Coverage**: %.1f%%\n", 
analysis.getSummary().getCodeCoverage()));
comment.append(String.format("- **Duplication**: %.1f%%\n", 
analysis.getSummary().getDuplicationPercentage()));
// Critical issues
List<CodeIssue> criticalIssues = analysis.getCriticalIssues();
if (!criticalIssues.isEmpty()) {
comment.append("\n### ⚠️ Critical Issues\n");
criticalIssues.forEach(issue -> 
comment.append(String.format("- `%s` %s\n", 
issue.getFormattedLocation(), issue.getMessage())));
}
// Security issues
List<CodeIssue> securityIssues = analysis.getSecurityIssues();
if (!securityIssues.isEmpty()) {
comment.append("\n### 🔒 Security Issues\n");
securityIssues.forEach(issue -> 
comment.append(String.format("- `%s` %s\n", 
issue.getFormattedLocation(), issue.getMessage())));
}
// Quality gates
comment.append("\n### 🎯 Quality Gates\n");
analysis.getQualityGates().forEach(gate -> {
String status = gate.isPassed() ? "✅" : "❌";
comment.append(String.format("- %s %s\n", status, gate.getName()));
});
return comment.toString();
}
private String generateStatusDescription(QualityGateStatus status, CodacyAnalysisResult analysis) {
if (status.isPassed()) {
return String.format("Quality gates passed (%d issues)", 
analysis.getSummary().getTotalIssues());
} else {
return String.format("Quality gates failed (%d issues)", 
analysis.getSummary().getTotalIssues());
}
}
private String mapToGitHubState(QualityGateStatus status) {
return status.isPassed() ? "success" : "failure";
}
private String generateDetailsUrl(PullRequestEvent event, CodacyAnalysisResult analysis) {
return String.format("https://app.codacy.com/analysis/%s/%s", 
event.getRepository(), analysis.getCommitId());
}
private void createErrorStatus(PullRequestEvent event, String errorMessage) {
StatusCheck status = new StatusCheck();
status.setContext("codacy/quality");
status.setDescription("Quality analysis failed: " + errorMessage);
status.setState("error");
githubClient.createStatus(event.getRepository(), event.getHeadCommit(), status);
}
}
@Data
public class PullRequestEvent {
private String repository;
private int prNumber;
private String branch;
private String headCommit;
private String baseCommit;
private String author;
private List<String> changedFiles;
private Date createdAt;
}
@Data
public class StatusCheck {
private String context;
private String description;
private String state; // success, failure, error, pending
private String targetUrl;
}

6. Quality Gates and Enforcement

Implement configurable quality gates.

Quality Gate Service:

@Service
@Slf4j
public class QualityGateService {
private final Map<String, QualityGate> qualityGates;
private final CodacyIntegrationService codacyService;
public QualityGateService(CodacyIntegrationService codacyService) {
this.codacyService = codacyService;
this.qualityGates = loadQualityGates();
}
/**
* Evaluate all quality gates for a commit
*/
public QualityGateResult evaluateAllGates(String commitHash) {
CodacyAnalysisResult analysis = codacyService.getAnalysisResults(commitHash);
List<GateEvaluation> evaluations = qualityGates.values().stream()
.map(gate -> evaluateGate(gate, analysis))
.collect(Collectors.toList());
boolean allPassed = evaluations.stream().allMatch(GateEvaluation::isPassed);
return new QualityGateResult(allPassed, evaluations);
}
/**
* Evaluate specific quality gate
*/
public GateEvaluation evaluateGate(String gateName, String commitHash) {
QualityGate gate = qualityGates.get(gateName);
if (gate == null) {
throw new IllegalArgumentException("Unknown quality gate: " + gateName);
}
CodacyAnalysisResult analysis = codacyService.getAnalysisResults(commitHash);
return evaluateGate(gate, analysis);
}
/**
* Check if commit can be merged based on quality gates
*/
public MergeApproval checkMergeApproval(String commitHash, String targetBranch) {
QualityGateResult gateResult = evaluateAllGates(commitHash);
MergeApproval approval = new MergeApproval();
approval.setCommitHash(commitHash);
approval.setTargetBranch(targetBranch);
approval.setApproved(gateResult.isAllPassed());
approval.setEvaluationDate(new Date());
approval.setGateResults(gateResult);
if (!gateResult.isAllPassed()) {
approval.setRejectionReason(generateRejectionReason(gateResult));
}
return approval;
}
/**
* Get quality gate configuration
*/
public QualityGate getQualityGate(String name) {
return qualityGates.get(name);
}
/**
* Update quality gate configuration
*/
public void updateQualityGate(String name, QualityGate gate) {
qualityGates.put(name, gate);
saveQualityGates();
}
private GateEvaluation evaluateGate(QualityGate gate, CodacyAnalysisResult analysis) {
List<ConditionResult> conditionResults = gate.getConditions().stream()
.map(condition -> evaluateCondition(condition, analysis))
.collect(Collectors.toList());
boolean passed = conditionResults.stream().allMatch(ConditionResult::isPassed);
return new GateEvaluation(gate.getName(), passed, conditionResults);
}
private ConditionResult evaluateCondition(QualityCondition condition, 
CodacyAnalysisResult analysis) {
double actualValue = getMetricValue(condition.getMetric(), analysis);
boolean passed = evaluateCondition(condition, actualValue);
return new ConditionResult(condition, actualValue, passed);
}
private double getMetricValue(String metric, CodacyAnalysisResult analysis) {
switch (metric) {
case "total_issues":
return analysis.getSummary().getTotalIssues();
case "critical_issues":
return analysis.getSummary().getCriticalIssues();
case "security_issues":
return analysis.getSecurityIssues().size();
case "code_coverage":
return analysis.getSummary().getCodeCoverage();
case "duplication":
return analysis.getSummary().getDuplicationPercentage();
case "complexity":
return analysis.getSummary().getComplexityAverage();
case "comment_density":
return analysis.getMetrics().getCommentDensityPercentage();
default:
throw new IllegalArgumentException("Unknown metric: " + metric);
}
}
private boolean evaluateCondition(QualityCondition condition, double actualValue) {
switch (condition.getOperator()) {
case "less_than":
return actualValue < condition.getThreshold();
case "less_than_equal":
return actualValue <= condition.getThreshold();
case "greater_than":
return actualValue > condition.getThreshold();
case "greater_than_equal":
return actualValue >= condition.getThreshold();
case "equals":
return actualValue == condition.getThreshold();
default:
throw new IllegalArgumentException("Unknown operator: " + condition.getOperator());
}
}
private Map<String, QualityGate> loadQualityGates() {
Map<String, QualityGate> gates = new HashMap<>();
// Security Gate
gates.put("security", createSecurityGate());
// Code Quality Gate
gates.put("code_quality", createCodeQualityGate());
// Coverage Gate
gates.put("coverage", createCoverageGate());
// Complexity Gate
gates.put("complexity", createComplexityGate());
return gates;
}
private QualityGate createSecurityGate() {
QualityGate gate = new QualityGate();
gate.setName("Security Gate");
gate.setDescription("No security vulnerabilities allowed");
gate.setCritical(true);
gate.setConditions(List.of(
new QualityCondition("security_issues", "equals", 0, 
"No security vulnerabilities allowed")
));
return gate;
}
private QualityGate createCodeQualityGate() {
QualityGate gate = new QualityGate();
gate.setName("Code Quality Gate");
gate.setDescription("Maintain code quality standards");
gate.setConditions(List.of(
new QualityCondition("critical_issues", "equals", 0, 
"No critical issues allowed"),
new QualityCondition("duplication", "less_than", 5, 
"Duplication less than 5%"),
new QualityCondition("total_issues", "less_than", 10, 
"Total issues less than 10")
));
return gate;
}
// Other gate creation methods...
}
@Data
public class QualityGate {
private String name;
private String description;
private boolean critical;
private List<QualityCondition> conditions;
private Date created;
private Date updated;
}
@Data
public class QualityCondition {
private String metric;
private String operator; // less_than, greater_than, equals
private double threshold;
private String description;
public QualityCondition(String metric, String operator, double threshold, String description) {
this.metric = metric;
this.operator = operator;
this.threshold = threshold;
this.description = description;
}
}
@Data
public class QualityGateResult {
private boolean allPassed;
private List<GateEvaluation> gateEvaluations;
private Date evaluationDate;
public QualityGateResult(boolean allPassed, List<GateEvaluation> gateEvaluations) {
this.allPassed = allPassed;
this.gateEvaluations = gateEvaluations;
this.evaluationDate = new Date();
}
}
@Data
public class GateEvaluation {
private String gateName;
private boolean passed;
private List<ConditionResult> conditionResults;
private String message;
public GateEvaluation(String gateName, boolean passed, List<ConditionResult> conditionResults) {
this.gateName = gateName;
this.passed = passed;
this.conditionResults = conditionResults;
this.message = passed ? "Gate passed" : "Gate failed";
}
}

7. Dashboard and Reporting

Create comprehensive quality dashboards and reports.

Dashboard Service:

@Service
@Slf4j
public class QualityDashboardService {
private final CodacyIntegrationService codacyService;
private final QualityAnalysisService qualityService;
private final MetricsRepository metricsRepository;
public QualityDashboardService(CodacyIntegrationService codacyService,
QualityAnalysisService qualityService,
MetricsRepository metricsRepository) {
this.codacyService = codacyService;
this.qualityService = qualityService;
this.metricsRepository = metricsRepository;
}
/**
* Generate comprehensive quality dashboard
*/
public QualityDashboard generateDashboard(String project, String branch, int days) {
QualityTrends trends = codacyService.getQualityTrends(branch, days);
List<CodacyAnalysisResult> recentAnalyses = getRecentAnalyses(project, branch, days);
QualityDashboard dashboard = new QualityDashboard();
dashboard.setProject(project);
dashboard.setBranch(branch);
dashboard.setPeriodDays(days);
dashboard.setGeneratedDate(new Date());
// Current quality metrics
dashboard.setCurrentQuality(getCurrentQuality(recentAnalyses));
// Trends and insights
dashboard.setQualityTrends(analyzeQualityTrends(trends));
dashboard.setImprovementAreas(identifyDashboardImprovementAreas(trends));
// Team metrics
dashboard.setTeamMetrics(calculateTeamMetrics(recentAnalyses));
// Recommendations
dashboard.setRecommendations(generateDashboardRecommendations(trends));
return dashboard;
}
/**
* Generate quality report for stakeholders
*/
public StakeholderReport generateStakeholderReport(String project, int days) {
QualityTrends trends = codacyService.getQualityTrends("main", days);
QualityTrendsAnalysis analysis = qualityService.analyzeQualityTrends("main", days);
StakeholderReport report = new StakeholderReport();
report.setProject(project);
report.setPeriodDays(days);
report.setReportDate(new Date());
// Executive summary
report.setExecutiveSummary(generateExecutiveSummary(analysis));
// Key metrics
report.setKeyMetrics(extractKeyMetrics(trends));
// Risk assessment
report.setRiskAssessment(assessQualityRisks(analysis));
// Investment recommendations
report.setInvestmentRecommendations(generateInvestmentRecommendations(analysis));
return report;
}
/**
* Generate team quality report
*/
public TeamQualityReport generateTeamReport(String team, int days) {
List<ProjectQuality> projectQualities = getTeamProjectQualities(team, days);
TeamQualityReport report = new TeamQualityReport();
report.setTeam(team);
report.setPeriodDays(days);
report.setReportDate(new Date());
// Team overview
report.setTeamOverview(createTeamOverview(projectQualities));
// Performance metrics
report.setPerformanceMetrics(calculateTeamPerformance(projectQualities));
// Improvement opportunities
report.setImprovementOpportunities(identifyTeamImprovements(projectQualities));
// Comparative analysis
report.setComparativeAnalysis(compareTeamPerformance(team, projectQualities));
return report;
}
private QualityMetrics getCurrentQuality(List<CodacyAnalysisResult> analyses) {
if (analyses.isEmpty()) {
return new QualityMetrics();
}
CodacyAnalysisResult latest = analyses.get(0);
QualityMetrics metrics = new QualityMetrics();
metrics.setQualityScore(latest.getSummary().getQualityScore());
metrics.setCodeCoverage(latest.getSummary().getCodeCoverage());
metrics.setDuplication(latest.getSummary().getDuplicationPercentage());
metrics.setComplexity(latest.getSummary().getComplexityAverage());
metrics.setTotalIssues(latest.getSummary().getTotalIssues());
metrics.setSecurityIssues(latest.getSecurityIssues().size());
return metrics;
}
private List<DashboardRecommendation> generateDashboardRecommendations(QualityTrends trends) {
List<DashboardRecommendation> recommendations = new ArrayList<>();
// Analyze trends and generate recommendations
if (trends.getCoverageTrend() < 0) {
recommendations.add(new DashboardRecommendation(
"Test Coverage", "HIGH",
"Test coverage is decreasing. Consider adding more unit tests.",
"Add test cases for new features and improve existing test coverage"
));
}
if (trends.getDuplicationTrend() > 0) {
recommendations.add(new DashboardRecommendation(
"Code Duplication", "MEDIUM",
"Code duplication is increasing. Consider refactoring common code.",
"Identify and extract duplicated code into reusable components"
));
}
if (trends.getIssueTrend() > 0) {
recommendations.add(new DashboardRecommendation(
"Code Quality", "HIGH",
"Number of code issues is increasing. Review new issues regularly.",
"Implement code review process and address issues before merging"
));
}
return recommendations;
}
// Additional dashboard generation methods...
}
@Data
public class QualityDashboard {
private String project;
private String branch;
private int periodDays;
private Date generatedDate;
private QualityMetrics currentQuality;
private QualityTrendsAnalysis qualityTrends;
private List<ImprovementArea> improvementAreas;
private TeamMetrics teamMetrics;
private List<DashboardRecommendation> recommendations;
}
@Data
public class StakeholderReport {
private String project;
private int periodDays;
private Date reportDate;
private String executiveSummary;
private Map<String, Object> keyMetrics;
private RiskAssessment riskAssessment;
private List<InvestmentRecommendation> investmentRecommendations;
}
@Data
public class TeamQualityReport {
private String team;
private int periodDays;
private Date reportDate;
private TeamOverview teamOverview;
private PerformanceMetrics performanceMetrics;
private List<ImprovementOpportunity> improvementOpportunities;
private ComparativeAnalysis comparativeAnalysis;
}
@Data
public class DashboardRecommendation {
private String category;
private String priority; // HIGH, MEDIUM, LOW
private String description;
private String action;
private Date suggestedCompletion;
public DashboardRecommendation(String category, String priority, 
String description, String action) {
this.category = category;
this.priority = priority;
this.description = description;
this.action = action;
this.suggestedCompletion = Date.from(
Instant.now().plus(7, ChronoUnit.DAYS)); // Default 1 week
}
}

8. CI/CD Integration

Integrate Codacy quality checks into CI/CD pipelines.

GitHub Actions Workflow:

name: Codacy Quality Check
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
codacy-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Java
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B compile test-compile
- name: Run Codacy Analysis
uses: codacy/codacy-analysis-cli-action@v4
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
max-allowed-issues: 10
fail-on-duplication: true
fail-on-coverage-decrease: true
- name: Upload Coverage to Codacy
run: |
mvn jacoco:report
bash <(curl -Ls https://coverage.codacy.com/get.sh) report -l Java -r target/site/jacoco/jacoco.xml
- name: Quality Gate Check
run: |
# Custom script to check quality gates
./scripts/check-quality-gates.sh

Jenkins Pipeline:

pipeline {
agent any
tools {
maven 'Maven-3.8'
jdk 'JDK-17'
}
stages {
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Test') {
steps {
sh 'mvn test'
jacoco(
execPattern: 'target/**/*.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java'
)
}
}
stage('Codacy Analysis') {
steps {
withCredentials([string(credentialsId: 'codacy-project-token', variable: 'CODACY_TOKEN')]) {
sh '''
mvn com.codacy:codacy-maven-plugin:2.2.0:coverage \
-DcoverageReportFile=target/site/jacoco/jacoco.xml \
-DprojectToken=${CODACY_TOKEN} \
-DapiToken=${CODACY_TOKEN}
mvn com.codacy:codacy-maven-plugin:2.2.0:analysis \
-DprojectToken=${CODACY_TOKEN} \
-DfailOnDuplication=true \
-DfailOnQualityGate=true
'''
}
}
}
stage('Quality Gate') {
steps {
script {
def qualityGate = codacyQualityGate()
if (!qualityGate.passed) {
error "Quality gate failed: ${qualityGate.reason}"
}
}
}
}
}
post {
always {
// Send quality report
emailext (
subject: "Build ${currentBuild.result}: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
Quality Report for ${env.JOB_NAME} #${env.BUILD_NUMBER}
Result: ${currentBuild.result}
Quality Score: ${qualityGate.score}
Issues Found: ${qualityGate.issues}
Coverage: ${qualityGate.coverage}%
${qualityGate.reportUrl}
""",
to: "[email protected]"
)
}
}
}

Best Practices for Codacy in Java

  1. Incremental Adoption: Start with critical rules and gradually add more
  2. Custom Rules: Create project-specific rules when needed
  3. Team Education: Train developers on understanding and fixing issues
  4. Quality Gates: Set realistic but meaningful quality thresholds
  5. Regular Review: Schedule regular code quality reviews
  6. Integration: Embed quality checks in development workflow
  7. Metrics Tracking: Monitor quality trends over time

Conclusion: Continuous Quality Improvement

Codacy provides a comprehensive platform for maintaining and improving Java code quality through automated analysis, quality gates, and detailed reporting. By integrating Codacy into your development workflow, teams can catch issues early, maintain consistent quality standards, and make data-driven decisions about code improvements.

This implementation demonstrates that effective Codacy usage transforms code quality from a subjective assessment into a measurable, actionable metric that drives continuous improvement and technical excellence across Java development teams.

Secure Java Dependency Management, Vulnerability Scanning & Software Supply Chain Protection (SBOM, SCA, CI Security & License Compliance)

https://macronepal.com/blog/github-code-scanning-in-java-complete-guide/
Explains GitHub Code Scanning for Java using tools like CodeQL to automatically analyze source code and detect security vulnerabilities directly inside CI/CD pipelines before deployment.

https://macronepal.com/blog/license-compliance-in-java-comprehensive-guide/
Explains software license compliance in Java projects, ensuring dependencies follow legal requirements (MIT, Apache, GPL, etc.) and preventing license violations in enterprise software.

https://macronepal.com/blog/container-security-for-java-uncovering-vulnerabilities-with-grype/
Explains using Grype to scan Java container images and filesystems for known CVEs in OS packages and application dependencies to improve container security.

https://macronepal.com/blog/syft-sbom-generation-in-java-comprehensive-software-bill-of-materials-for-jvm-applications/
Explains using Syft to generate SBOMs (Software Bill of Materials) for Java applications, listing all dependencies, libraries, and components for supply chain transparency.

https://macronepal.com/blog/comprehensive-dependency-analysis-generating-and-scanning-sboms-with-trivy-for-java/
Explains using Trivy to generate SBOMs and scan Java dependencies and container images for vulnerabilities, integrating security checks into CI/CD pipelines.

https://macronepal.com/blog/dependabot-for-java-in-java/
Explains GitHub Dependabot for Java projects, which automatically detects vulnerable dependencies and creates pull requests to update them securely.

https://macronepal.com/blog/parasoft-jtest-in-java-comprehensive-guide-to-code-analysis-and-testing/
Explains Parasoft Jtest, a static analysis and testing tool for Java that helps detect bugs, security issues, and code quality problems early in development.

https://macronepal.com/blog/snyk-open-source-in-java-comprehensive-dependency-vulnerability-management-2/
Explains Snyk Open Source for Java, which continuously scans dependencies for vulnerabilities and provides automated fix suggestions and monitoring.

https://macronepal.com/blog/owasp-dependency-check-in-java-complete-vulnerability-scanning-guide/
Explains OWASP Dependency-Check, which scans Java dependencies against the National Vulnerability Database (NVD) to detect known security vulnerabilities.

https://macronepal.com/blog/securing-your-dependencies-a-java-developers-guide-to-whitesource-mend-bolt/
Explains Mend (WhiteSource) Bolt for Java, a dependency management and SCA tool that provides vulnerability detection, license compliance, and security policy enforcement in enterprise environments.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper