DevOps

How I built MergeMate: a Serverless Code Review Assistant

Walk through how our team built a serverless code review assistant using AWS Bedrock and GitLab. See how we tackled the challenges of code reviews at scale and turned them into a streamlined, automated process.

Published December 15, 2024 β€’ 12 min read

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.