Code reviews are essential for maintaining code quality, but they can become a bottleneck as teams grow. Our team built MergeMate, a serverless code review assistant that uses AWS Bedrock to provide intelligent, automated code review feedback integrated directly into GitLab merge requests.
The Problem
Our development team was facing several challenges with code reviews:
- Inconsistent review quality across different reviewers
- Long wait times for senior developers to review junior code
- Repetitive feedback on common issues (style, best practices)
- Difficulty maintaining coding standards across multiple projects
Architecture Overview
MergeMate uses a serverless architecture built on AWS:
GitLab Webhook β API Gateway β Lambda Function β Bedrock β GitLab API
β
S3 (Code Context) β DynamoDB (Review History)
Key Components
- GitLab Webhooks: Trigger reviews on merge request creation
- AWS Lambda: Process webhook events and coordinate reviews
- Amazon Bedrock: Generate intelligent code review comments
- S3: Store code context and diff information
- DynamoDB: Track review history and learning patterns
Implementation Deep Dive
1. Webhook Processing
import json
import boto3
def lambda_handler(event, context):
# Parse GitLab webhook
webhook_data = json.loads(event['body'])
if webhook_data.get('object_kind') != 'merge_request':
return {'statusCode': 200, 'body': 'Not a merge request'}
mr_data = webhook_data['object_attributes']
project_id = webhook_data['project']['id']
mr_iid = mr_data['iid']
# Queue review job
process_merge_request(project_id, mr_iid)
return {'statusCode': 200, 'body': 'Review queued'}
2. Code Analysis
def analyze_code_changes(project_id, mr_iid):
# Get diff from GitLab API
gitlab = gitlab_client()
mr = gitlab.projects.get(project_id).mergerequests.get(mr_iid)
changes = mr.changes()
analysis_context = {
'files_changed': len(changes['changes']),
'additions': 0,
'deletions': 0,
'code_snippets': []
}
for change in changes['changes']:
if change['new_file'] or change['deleted_file']:
continue
# Analyze each chunk of changes
diff_content = change['diff']
analysis_context['code_snippets'].append({
'file': change['new_path'],
'diff': diff_content,
'language': detect_language(change['new_path'])
})
return analysis_context
3. AI-Powered Review Generation
def generate_review_comments(analysis_context):
bedrock = boto3.client('bedrock-runtime')
prompt = f"""
You are an experienced software engineer reviewing code changes.
Analyze the following code diff and provide constructive feedback.
Focus on:
- Code quality and best practices
- Security vulnerabilities
- Performance considerations
- Maintainability issues
Code changes:
{format_code_context(analysis_context)}
Provide specific, actionable feedback with line numbers where applicable.
"""
response = bedrock.invoke_model(
modelId='anthropic.claude-3-sonnet-20240229-v1:0',
contentType='application/json',
accept='application/json',
body=json.dumps({
'anthropic_version': 'bedrock-2023-05-31',
'max_tokens': 2000,
'messages': [{'role': 'user', 'content': prompt}]
})
)
return parse_ai_response(response)
Smart Comment Filtering
Not every AI suggestion is valuable. We implemented filtering to ensure quality:
def filter_comments(comments, context):
filtered = []
for comment in comments:
# Skip if similar comment exists in history
if is_duplicate_comment(comment, context['mr_history']):
continue
# Skip trivial style issues if project has auto-formatting
if has_auto_formatting(context['project']) and is_style_comment(comment):
continue
# Prioritize security and performance issues
comment['priority'] = calculate_priority(comment)
filtered.append(comment)
# Only post top priority comments to avoid noise
return sorted(filtered, key=lambda x: x['priority'], reverse=True)[:5]
GitLab Integration
def post_review_comments(project_id, mr_iid, comments):
gitlab = gitlab_client()
mr = gitlab.projects.get(project_id).mergerequests.get(mr_iid)
for comment in comments:
# Create threaded discussion on specific lines
mr.discussions.create({
'body': format_comment(comment),
'position': {
'position_type': 'text',
'new_path': comment['file_path'],
'new_line': comment['line_number']
}
})
# Add summary comment
summary = generate_review_summary(comments)
mr.notes.create({'body': summary})
Continuous Learning
MergeMate learns from developer interactions:
def track_comment_feedback(comment_id, feedback_type):
# Store in DynamoDB for learning
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('mergemate-feedback')
table.put_item(Item={
'comment_id': comment_id,
'feedback': feedback_type, # 'helpful', 'not_helpful', 'resolved'
'timestamp': int(time.time()),
'project_context': get_project_context()
})
# Update AI model preferences
update_review_patterns(feedback_type)
Results and Metrics
After 3 months of using MergeMate:
- 60% faster reviews: First-pass automated feedback reduces human review time
- 40% fewer defects: Catches common issues before human review
- 85% developer satisfaction: Helpful, actionable feedback
- $2,400/month cost: Much cheaper than additional senior developers
Key Learnings
- Context is crucial: Providing full file context, not just diffs, improves review quality
- Less is more: Limiting comments to top 5 issues prevents notification fatigue
- Learning loop: Tracking developer feedback continuously improves the system
- Integration matters: Seamless GitLab integration encourages adoption
Future Enhancements
We're working on:
- Custom rule sets per project or team
- Integration with static analysis tools
- Automated fix suggestions for common issues
- Performance impact analysis for larger changes
Conclusion
MergeMate has transformed our code review process from a bottleneck into an accelerator. By combining AI capabilities with smart filtering and continuous learning, we've created a tool that genuinely helps developers write better code faster.
The serverless architecture keeps costs low while scaling with our team's needs. Most importantly, developers actually enjoy using itβwhich is the ultimate measure of success for any developer tool.