Automated code review agent on Civo GPUs

Drop any source file into a bucket. Walk away with a structured, AI-generated review; issues, complexity, and actionable suggestions just seconds later.

11 minutes reading time

Written by

Mostafa Ibrahim
Mostafa Ibrahim

Software Engineer @ GoCardless

Code review is one of the most effective quality gates in software development, but in most teams, it is also one of the slowest. A 2023 LinearB study found that the average pull requests sit untouched for more than 24 hours before receiving a first response. SmartBear’s State of Code Review report reinforces the problem: reviewers are most effective when they examine fewer than 400 lines at a time, but the median PR in most teams exceeds that threshold, leading reviewers to either slow down or rush through their work. The downstream effect is significant; code review defects account for up to 65% of production bugs that make it past testing.

Manual-only review creates a predictable pattern: junior developers wait days for feedback on obvious mistakes. Reviewers, already stretched thin, skip structural analysis and focus only on surface errors. Issues such as SQL injection, hardcoded credentials, or weak cryptography slip through because nobody had time to look closely. The problem isn’t a lack of standards, it’s a lack of bandwidth.

This tutorial solves the problem. It walks through building a private, GPU-powered code-review agent that runs entirely within a Civo Kubernetes cluster. Upload any source file to a Civo Object Store bucket, and the pipeline automatically produces a structured JSON review: issues found (with severity and line references), an overall complexity rating, and a prioritized list of improvement suggestions. No external SaaS. No code leaving controlled infrastructure. The system operates as a first-pass reviewer; fast, consistent, and always available.

How the pipeline works

The system is built around a simple pattern: an Object Store bucket acts as the trigger, and two lightweight pods handle the work. 

Automated code review agent on Civo GPUs

The sequence from file upload to JSON output follows these steps:

  1. A source file: Python, JavaScript, Go, or any plain-text code format, is uploaded to the code-submissions bucket inside Civo Object Store.
  2. The watcher pod polls the bucket every 10 seconds. When it detects a file without a corresponding .review.json output, it downloads the file and forwards it to the reviewer pod.
  3. The reviewer pod sends a structured analysis prompt to relaxAI and receives a JSON object that contains issues, a complexity classification, and suggestions.
  4. The watcher uploads the result to the same bucket as <filename>.review.json. The original file and its review now sit side by side.

Three components make this work. The relaxAI Reviewer Pod is a FastAPI service running on a GPU node. It exposes a /review endpoint, handles the relaxAI call, strips markdown formatting from model output, and validates the response against a Pydantic schema. The Watcher Agent Pod is a standalone Python process that runs the polling loop. It handles file download, forwards files to the reviewer, and uploads results, keeping all orchestration logic separate from the inference logic. The Civo Object Store Bucket is the coordination layer between the two pods and the entry point for developers using the system.

Why this works well on Civo

Most cloud platforms make GPU access complicated, with quota requests, approval queues, and multi-day waits. Civo provisions GPU nodes in minutes with no approval process, so a cluster can be spun up for a batch of reviews and deleted when done. No idle costs, no bureaucracy.

Everything the pipeline needs, compute, Object Store, and networking, lives in the same regional environment and is managed from one dashboard. No cross-account policies, no external storage integrations, no IAM complexity. The watcher pod accesses the Object Store the same way it accesses any internal service: via a Kubernetes Secret containing credentials and a standard S3-compatible call.

The data boundary is the most important advantage for teams handling proprietary or regulated code. Source files travel from Object Store to the reviewer pod and back, entirely within the cluster. Nothing touches a third-party endpoint, which makes the setup defensible to security and compliance teams without any architectural changes.

And once this agent is running, the same setup bucket, watcher, and inference pod works for test generation, documentation drafting, or dependency analysis. Swap the prompt, rebuild the image, and a new tool is live.

Prerequisites

Before we dive in, make sure you have:

Don’t worry if you’re not a Kubernetes expert; every step is explained in detail.

Project Layout

Create this folder structure locally. Keeping the reviewer and watcher in separate directories makes Dockerfile scoping straightforward and keeps each service’s dependencies isolated.

code-reviewer/
├── reviewer/
│ ├── app.py
│ ├── requirements.txt
│ └── Dockerfile
├── watcher/
│ ├── watcher.py
│ ├── requirements.txt
│ └── Dockerfile
└── kubernetes/
├── namespace.yaml
├── secret.yaml
├── reviewer-deployment.yaml
└── watcher-deployment.yaml

Step 1: Provision and connect to the GPU cluster

Civo clusters with GPU nodes are provisioned in a few minutes. This step creates the cluster, downloads the kubeconfig file, and verifies that kubectl can reach the nodes.

  1. Log in to the Civo Dashboard and navigate to KubernetesCreate Cluster.
  2. Set the name to code-reviewer-cluster, keep the network set to Default, and select a GPU-enabled instance type for the node pool. One node is sufficient for this tutorial.
  3. Click Create Cluster and wait for the status indicator to show Ready (typically 2–5 minutes)
  4. Click Download Kubeconfig once the cluster is ready.

Connect kubectl to the new cluster by setting the KUBECONFIG environment variable to the path of the downloaded file:

# macOS / Linux
export KUBECONFIG=/path/to/downloaded/kubeconfig.yaml
# Windows
set KUBECONFIG=C:\path\to\downloaded\kubeconfig.yaml

Verify that the node is visible and ready:

kubectl get nodes
Automated code review agent on Civo GPUs

Create a dedicated namespace and set it as the active context so that subsequent kubectl commands do not need a -n flag:

kubectl create namespace code-reviewer
kubectl config set-context --current --namespace=code-reviewer

Step 2: Set up the Object Store bucket

The Object Store bucket serves as the shared file system between developers uploading code and the review pipeline. Civo’s Object Store is S3-compatible, so standard boto3 calls work against it without any additional configuration.

Create credentials

  1. In the Civo Dashboard, go to Object StoresCredentials.
  2. Click Create a new Credential.
  3. Save the Access Key and Secret Key securely; both are needed in Step 5 when configuring the Kubernetes Secret.

Create the store and bucket

  1. Go to Object StoresCreate Object Store.
  2. Name it code-review-store and select the same region as the Kubernetes cluster (for example, lon1).
  3. Click Create.
  4. Open the newly created store and click Create a new folder.
  5. Name the folder code-submissions and click Create folder.

The Object Store endpoint URL follows the format below. Note this value, it becomes the S3_ENDPOINT environment variable in Step 5.

https://objectstore.<region>.civo.com

Replace <region> with the actual region throughout the remaining steps.

Step 3: Build the reviewer service and watcher agent

Two services power the pipeline. The reviewer is a FastAPI application that handles AI inference. The watcher is a standalone script that handles orchestration. Separating them keeps each piece testable and independently deployable.

Reviewer service (reviewer/app.py)

The reviewer exposes a single /review endpoint. When called with a code file and a filename, it constructs a structured analysis prompt, sends it to relaxAI, cleans the model’s response, and returns a validated JSON object. The code is organized into four clearly labeled sections, making each responsibility easy to locate.

Create reviewer/app.py

Setup & configuration:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from relaxai import Relaxai
import os, json
from typing import List
app = FastAPI()
RELAXAI_API_KEY = os.getenv("RELAXAI_API_KEY")
client = Relaxai(api_key=RELAXAI_API_KEY) if RELAXAI_API_KEY else None

Data models:

class ReviewRequest(BaseModel):
content: str
filename: str
class Issue(BaseModel):
severity: str # high | medium | low
line: str # e.g. 'line 14' or 'general'
description: str
class ReviewResponse(BaseModel):
filename: str
language: str
complexity: str # low | medium | high
issues: List[Issue]
suggestions: List[str]
summary: str

Endpoints:

@app.get("/health")
async def health():
return {"status": "healthy", "relaxai_configured": client is not None}
@app.post("/review", response_model=ReviewResponse)
async def review(request: ReviewRequest):
if not client:
raise HTTPException(500, "RelaxAI API key not configured")
content_preview = request.content[:8000]
prompt = f"""
Analyze the following code file and return a structured code review.
File: {request.filename}
Content:
{content_preview}
Respond ONLY with valid JSON in this exact format:
{{
"language": "detected language",
"complexity": "low | medium | high",
"issues": [
{{"severity": "high|medium|low",
"line": "line X or general",
"description": "clear description"}}
],
"suggestions": ["actionable improvement"],
"summary": "2-sentence overall assessment"
}}
Return ONLY the JSON. No explanation.
"""

relaxAI call & response parsing:

try:
completion = client.chat.create_completion(
messages=[
{"role": "system",
"content": "You are an expert code reviewer. Return valid JSON only."},
{"role": "user", "content": prompt}
],
model="Llama-4-Maverick-17B-128E",
temperature=0.2,
max_tokens=800
)
resp = completion.choices[0].message.content.strip()
# Strip markdown fences if present
if resp.startswith("```json"): resp = resp[7:]
if resp.startswith("```"): resp = resp[3:]
if resp.endswith("```"): resp = resp[:-3]
resp = resp.strip()
data = json.loads(resp)
data["filename"] = request.filename
return ReviewResponse(**data)
except json.JSONDecodeError as e:
raise HTTPException(500, f"Failed to parse AI response: {e}")
except Exception as e:
raise HTTPException(500, f"Review failed: {e}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

Create reviewer/requirements.txt:

fastapi==0.104.1
uvicorn==0.24.0
relaxai
pydantic==2.5.0

Create reviewer/Dockerfile:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]

Watcher Agent (watcher/watcher.py)

The watcher runs a continuous polling loop. Every 10 seconds, it lists the bucket contents, identifies files that are missing a corresponding .review.json, and processes each one by downloading it, calling the reviewer, and uploading the result. Keeping this logic separate from the reviewer means each service has a single responsibility and can be debugged independently.

Create watcher/watcher.py

Setup & S3 client:

import boto3, httpx, json, time, os
from botocore.client import Config
S3_ENDPOINT = os.getenv("S3_ENDPOINT")
S3_ACCESS_KEY = os.getenv("S3_ACCESS_KEY")
S3_SECRET_KEY = os.getenv("S3_SECRET_KEY")
S3_BUCKET = os.getenv("S3_BUCKET", "code-submissions")
REVIEWER_URL = os.getenv("REVIEWER_URL", "http://reviewer-service:8000")
POLL_INTERVAL = int(os.getenv("POLL_INTERVAL", "10"))
s3 = boto3.client(
's3',
endpoint_url=S3_ENDPOINT,
aws_access_key_id=S3_ACCESS_KEY,
aws_secret_access_key=S3_SECRET_KEY,
config=Config(signature_version='s3v4')
)

Helper functions:

SUPPORTED = ('.py', '.js', '.ts', '.java', '.go', '.rb',
'.php', '.c', '.cpp', '.cs', '.rs', '.sh')
def get_file_content(key):
try:
body = s3.get_object(Bucket=S3_BUCKET, Key=key)['Body'].read()
try: return body.decode('utf-8')
except: return body.decode('latin-1')
except Exception as e:
print(f"Error downloading {key}: {e}")
return None
def upload_json(key, data):
try:
s3.put_object(Bucket=S3_BUCKET, Key=key,
Body=json.dumps(data, indent=2),
ContentType='application/json')
print(f"✓ Uploaded {key}")
except Exception as e:
print(f"Error uploading {key}: {e}")
def needs_review(filename, existing):
if filename.endswith('.json'): return False
if not filename.lower().endswith(SUPPORTED): return False
return f"{filename}.review.json" not in existing

File processing:

def process_file(filename):
print(f"Processing {filename}...")
content = get_file_content(filename)
if not content:
print(f"✗ Could not read {filename}")
return
try:
with httpx.Client(timeout=120.0) as c:
r = c.post(f"{REVIEWER_URL}/review",
json={"content": content, "filename": filename})
r.raise_for_status()
review = r.json()
upload_json(f"{filename}.review.json", review)
print(f"✓ Done -- {len(review.get('issues', []))} issue(s) found")
except httpx.TimeoutException:
print(f"✗ Timeout processing {filename}")
except Exception as e:
print(f"✗ Failed: {e}")

Poll loop:

def watch_bucket():
print("=" * 60)
print("CODE REVIEWER WATCHER STARTED")
print(f"Bucket: {S3_BUCKET} | Interval: {POLL_INTERVAL}s")
print("=" * 60)
while True:
try:
resp = s3.list_objects_v2(Bucket=S3_BUCKET)
if 'Contents' not in resp:
print("Bucket empty, waiting...")
time.sleep(POLL_INTERVAL)
continue
existing = {o['Key'] for o in resp['Contents']}
to_review = [f for f in existing if needs_review(f, existing)]
if to_review:
print(f"\nFound {len(to_review)} file(s) to review")
for f in to_review: process_file(f)
else:
print(".", end="", flush=True)
except Exception as e:
print(f"\n✗ Watch loop error: {e}")
time.sleep(POLL_INTERVAL)
if __name__ == "__main__":
watch_bucket()

Create watcher/requirements.txt:

boto3==1.29.7
httpx==0.25.1

Create watcher/Dockerfile:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY watcher.py .
CMD ["python", "watcher.py"]

Step 4: Containerize both services

Both services need to be packaged as container images and pushed to a registry so Kubernetes can pull them during deployment. Docker Hub works well for this tutorial. Run the following commands from the root of the project directory:

# Log in to Docker Hub
docker login
# Build and push the reviewer image
cd reviewer
docker build -t yourusername/civo-code-reviewer:latest .
docker push yourusername/civo-code-reviewer:latest
# Build and push the watcher image
cd ../watcher
docker build -t yourusername/civo-code-watcher:latest .
docker push yourusername/civo-code-watcher:latest
cd ..

Step 5: Deploy to the cluster

Four Kubernetes manifests bring the pipeline to life: a namespace, a secret, a reviewer deployment with its ClusterIP service, and a watcher deployment. They are applied in that order so that the secret exists before the pods start.

namespace.yaml

A namespace keeps the review agent’s resources isolated from anything else running on the cluster, making cleanup and access control straightforward.

apiVersion: v1
kind: Namespace
metadata:
name: code-reviewer

secret.yaml

A Kubernetes Secret stores sensitive values, API keys, and Object Store credentials, separately from the container image. The pods read these values at startup through environment variable references. Replace all placeholder values with the actual credentials from Steps 2 and 3.

apiVersion: v1
kind: Secret
metadata:
name: reviewer-secrets
namespace: code-reviewer
type: Opaque
stringData:
S3_ENDPOINT: "https://objectstore.lon1.civo.com" # update region
S3_ACCESS_KEY: "your-civo-access-key"
S3_SECRET_KEY: "your-civo-secret-key"
S3_BUCKET: "code-submissions"
RELAXAI_API_KEY: "your-relaxai-api-key"

reviewer-deployment.yaml

The reviewer pod runs on a GPU node and exposes port 8000 through a ClusterIP service. A ClusterIP service makes the reviewer accessible only from other pods within the cluster, intentionally preventing external access to the source code. Liveness and readiness probes hit the /health endpoint to keep Kubernetes informed of the pod’s state.

apiVersion: apps/v1
kind: Deployment
metadata:
name: reviewer
namespace: code-reviewer
spec:
replicas: 1
selector:
matchLabels:
app: reviewer
template:
metadata:
labels:
app: reviewer
spec:
containers:
- name: reviewer
image: yourusername/civo-code-reviewer:latest
ports:
- containerPort: 8000
env:
- name: RELAXAI_API_KEY
valueFrom:
secretKeyRef:
name: reviewer-secrets
key: RELAXAI_API_KEY
resources:
requests:
memory: "2Gi"
cpu: "1000m"
nvidia.com/gpu: "1"
limits:
memory: "4Gi"
cpu: "2000m"
nvidia.com/gpu: "1"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
nodeSelector:
node.kubernetes.io/instance-type: g4-g.4xlarge.kube.cluster
---
apiVersion: v1
kind: Service
metadata:
name: reviewer-service
namespace: code-reviewer
spec:
selector:
app: reviewer
ports:
- protocol: TCP
port: 8000
targetPort: 8000
type: ClusterIP

watcher-deployment.yaml

The watcher pod reads all five secrets through environment variable references and sets REVIEWER_URL to the internal DNS name of the reviewer service. No GPU resources are needed here; the watcher is doing file I/O and HTTP calls, not inference.

apiVersion: apps/v1
kind: Deployment
metadata:
name: watcher
namespace: code-reviewer
spec:
replicas: 1
selector:
matchLabels:
app: watcher
template:
metadata:
labels:
app: watcher
spec:
containers:
- name: watcher
image: yourusername/civo-code-watcher:latest
env:
- name: S3_ENDPOINT
valueFrom:
secretKeyRef:
name: reviewer-secrets
key: S3_ENDPOINT
- name: S3_ACCESS_KEY
valueFrom:
secretKeyRef:
name: reviewer-secrets
key: S3_ACCESS_KEY
- name: S3_SECRET_KEY
valueFrom:
secretKeyRef:
name: reviewer-secrets
key: S3_SECRET_KEY
- name: S3_BUCKET
valueFrom:
secretKeyRef:
name: reviewer-secrets
key: S3_BUCKET
- name: REVIEWER_URL
value: "http://reviewer-service:8000"
- name: POLL_INTERVAL
value: "10"
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"

Apply and verify

Apply the manifests in order. The secret must exist before the pods start, or they will fail to read environment variables at launch.

kubectl apply -f kubernetes/namespace.yaml
kubectl config set-context --current --namespace=code-reviewer
# Secret must be applied before the pods start
kubectl apply -f kubernetes/secret.yaml
kubectl apply -f kubernetes/reviewer-deployment.yaml
kubectl apply -f kubernetes/watcher-deployment.yaml
kubectl get pods -n code-reviewer -w
Automated code review agent on Civo GPUs

Once both pods show Running, check the watcher logs to confirm the polling loop has started:

kubectl logs -f deployment/watcher -n code-reviewer
Automated code review agent on Civo GPUs

Step 6: Test the pipeline end-to-end

With both pods running, it’s time to put the pipeline through a real test. Rather than using a minimal toy example, this step uses a publicly available Flask application that contains several well-documented vulnerability classes. This gives the reviewer enough material to generate a meaningful, production-grade analysis, the kind of output that would actually be useful on a real team.

The test file

The code below is adapted from OWASP’s publicly documented insecure application examples. It’s a short Flask app, but it contains five distinct issues spanning injection, authentication, and information disclosure. Exactly the kind of problems a first-pass reviewer should catch before a human ever opens the PR.

Save this as vulnerable_api.py:

# Source: adapted from OWASP WebGoat Python examples (public domain)
from flask import Flask, request, render_template_string
import sqlite3
import subprocess
import hashlib
import os
app = Flask(__name__)
SECRET_KEY = "hardcoded_secret_abc123"
DB_PATH = "users.db"
@app.route('/login', methods=['POST'])
def login():
username = request.form['username']
password = request.form['password']
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
cursor.execute(query)
user = cursor.fetchone()
if user:
return render_template_string(f"<h1>Welcome {username}</h1>")
return "Login failed"
@app.route('/ping', methods=['GET'])
def ping():
host = request.args.get('host')
result = subprocess.check_output(f"ping -c 1 {host}", shell=True)
return result
@app.route('/hash', methods=['GET'])
def get_hash():
pwd = request.args.get('pwd')
return hashlib.md5(pwd.encode()).hexdigest()
@app.route('/debug', methods=['GET'])
def debug():
return str(os.environ)

Five issues are present in this file: a SQL injection vulnerability in the login route, a server-side template injection risk due to rendering unsanitised user input, a command injection vulnerability in the ping route, the use of MD5 for password-related hashing, and an exposed debug endpoint that leaks environment variables. The agent should surface all of them. 

Upload the file

  1. Open the Civo Dashboard and navigate to Object Storescode-review-storecode-submissions.
  2. Click Upload File, select vulnerable_api.py, and click Upload.

Watch the processing ogs

Switch to a terminal and watch the watcher logs. Within 10 seconds of the upload, the watcher should detect the file and forward it to the reviewer pod:

kubectl logs -f deployment/watcher -n code-reviewer
Automated code review agent on Civo GPUs

The review output

Refresh the Object Store bucket in the Civo Dashboard. Both files should now appear side by side: the original source file and the generated review.

Automated code review agent on Civo GPUs

Download vulnerable_api.py.review.json and open it. The full output from the agent should look similar to this:

{
"filename": "vulnerable_api.py",
"language": "Python",
"complexity": "medium",
"issues": [
{
"severity": "high",
"line": "line 17",
"description": "SQL injection: user input is concatenated directly into
the query string. Use parameterized queries instead."
},
{
"severity": "high",
"line": "line 21",
"description": "Server-side template injection: unsanitised username is
rendered via render_template_string. An attacker can
inject Jinja2 expressions and execute arbitrary code."
},
{
"severity": "high",
"line": "line 26",
"description": "Command injection: user-supplied host is passed to
subprocess with shell=True. Use a list argument instead."
},
{
"severity": "medium",
"line": "line 31",
"description": "MD5 is cryptographically broken. Replace with
hashlib.sha256 for any security-sensitive hashing."
},
{
"severity": "high",
"line": "line 35",
"description": "Debug endpoint exposes os.environ, leaking secrets,
API keys and infrastructure details. Remove before deploy."
}
],
"suggestions": [
"Replace string-formatted SQL with parameterized queries: cursor.execute(query, (username, password)).",
"Use render_template with a proper template file instead of render_template_string.",
"Rewrite the ping route to accept only validated input and call subprocess with a list, not a string.",
"Replace hashlib.md5 with hashlib.sha256 or hashlib.sha3_256.",
"Remove the /debug route entirely, or restrict it to authenticated internal users only.",
"Move SECRET_KEY to an environment variable loaded via os.getenv()."
],
"summary": "This file contains four high-severity vulnerabilities that would
allow remote code execution, data exfiltration, and authentication
bypass on any exposed deployment. None of these routes should be
reachable in production without significant remediation."
}

Troubleshooting

If the review JSON does not appear after 60 seconds, use the following commands to isolate the problem. The most common causes are incorrect Object Store credentials, a mismatched region in the S3 endpoint URL, or the reviewer pod still completing its startup health checks.

# Check pod status
kubectl get pods -n code-reviewer
# Check the reviewer health endpoint directly
kubectl port-forward deployment/reviewer 8000:8000 -n code-reviewer
curl http://localhost:8000/health
# Check watcher for errors
kubectl logs deployment/watcher -n code-reviewer | grep -i error
# Check reviewer logs
kubectl logs deployment/reviewer -n code-reviewer

Summary

A GPU-backed code review agent does not require enterprise infrastructure, a dedicated DevOps team, or an expensive SaaS subscription. The pattern built here, Object Store as the trigger, a watcher pod as the orchestrator, and an inference pod as the reviewer, is composable, lightweight, and entirely private.

A few observations worth carrying forward:

  • The polling pattern is a low-friction alternative to webhooks and event queues. It adds a maximum 10-second delay, which is acceptable for internal tooling, and requires no additional infrastructure beyond what is already deployed.
  • Separating the reviewer and watcher into independent services means each can be updated, debugged, or scaled without touching the other. Replacing the RelaxAI model or changing the prompt template requires only rebuilding the reviewer image.
  • The same architecture works for adjacent tasks. Swap the review prompt for a test-generation prompt, and the system produces unit test stubs. Swap it for a documentation prompt, and it generates inline comments. The watcher, secrets, and Object Store setup stay identical.
  • Because the cluster is already GPU-enabled, scaling from small files to large codebases or to batch processing requires no architectural changes — only adjustments to the replica count.

A natural next step for teams that find value in this tool is connecting the JSON output to a Civo Managed Database, building a searchable history of reviews that can surface recurring issues across a codebase over time.

Additional resources

The following links provide deeper context for the tools and concepts used in this tutorial.

    • Civo Kubernetes Documentation

    • Civo Object Store Guide

    • relaxAI API Documentation

    • Build an Auto-Summarise Folder Agent on Civo

Mostafa Ibrahim
Mostafa Ibrahim

Software Engineer @ GoCardless

Mostafa Ibrahim is a software engineer and technical writer specializing in developer-focused content for SaaS and AI platforms. He currently works as a Software Engineer at GoCardless, contributing to production systems and scalable payment infrastructure.

Alongside his engineering work, Mostafa has written more than 200 technical articles reaching over 500,000 readers. His content covers topics including Kubernetes deployments, AI infrastructure, authentication systems, and retrieval-augmented generation (RAG) architectures.

View author profile