×
Community Blog Real-World Projects and Optimization: Taking Your Solutions to the Next Level

Real-World Projects and Optimization: Taking Your Solutions to the Next Level

The article walks through building, optimizing, and scaling a production-grade URL shortener on Alibaba Cloud—from architecture and IaC to performance tuning and cost control.

Note: We've covered individual pieces of Alibaba Cloud. Now let's build something real. This final blog walks through a complete project from concept to optimization. By the end, you'll have a blueprint for your own applications.

Introduction

Theory is great. Practice is better.

In this blog, I'm going to walk you through building a real application: A URL Shortener Service (like bit.ly or TinyURL). It's complex enough to showcase most concepts we've covered, yet simple enough to understand completely.

You'll see:

● Architecture design from scratch

● Implementation step-by-step

● Real performance bottlenecks and how to fix them

● Cost optimization strategies

● Lessons learned

Let's build something.

Part 1: Project Requirements

What We're Building

A URL shortening service where:

  1. Users submit a long URL (e.g., https://example.com/very/long/url/with/parameters)
  2. System returns a short code (e.g., abc123)
  3. When anyone visits yourdomain.com/abc123, they're redirected to the long URL
  4. Users can track stats: how many times their link was clicked

Scale Expectations

● 1 million shortened URLs created per day

● 100 million redirect requests per day

● 99.9% availability (33 minutes downtime allowed per month)

● <100ms redirect latency required

Success Metrics

● Cost per million redirects (minimize)

● Latency (P95 < 50ms, P99 < 100ms)

● Availability (target 99.9%)

Part 2: Architecture Design

Data Model

Table: URLs

├─ id (auto-increment, primary key)
├─ short_code (unique, indexed)
├─ long_url (the original URL)
├─ user_id (who created it)
├─ created_at
├─ expires_at
└─ status (active, deleted)

Table: Statistics

├─ short_code (foreign key)
├─ visitor_count (total clicks)
├─ last_accessed_at
├─ unique_visitors (rough estimate)
└─ countries (top 5)

Short Code Generation

Challenge: Generate unique, random short codes that are URL-safe.

Algorithm:

  1. Start with a 6-character code (allows 62^6 = 56 billion combinations)
  2. Use charset: 0-9, a-z, A-Z (62 characters)
  3. Generate randomly until collision (rare, handled by retry)
  4. Store mapping: short_code → long_url

Code:

const CHARSET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

function generateShortCode(length = 6) {
  let code = '';
  for (let i = 0; i < length; i++) {
    code += CHARSET[Math.floor(Math.random() * CHARSET.length)];
  }
  return code;
}

async function createShortUrl(longUrl, userId) {
  let shortCode;
  let collision = false;
  
  do {
    shortCode = generateShortCode();
    // Check if exists
    const existing = await db.query('SELECT * FROM urls WHERE short_code = ?', shortCode);
    collision = existing.length > 0;
  } while (collision);
  
  // Store
  await db.query('INSERT INTO urls (short_code, long_url, user_id) VALUES (?, ?, ?)', 
    [shortCode, longUrl, userId]);
  
  return shortCode;
}

The Architecture

┌────────────────────────────────────────┐
│  User's Browser                        │
│  GET /abc123 (browser follows redirect)│
└──────────┬─────────────────────────────┘
           │
┌──────────▼──────────────────────────────┐
│  CDN (Cache redirects)                  │
│  Most hits stop here (80% cache hit)   │
└──────────┬──────────────────────────────┘
           │ (Cache miss)
┌──────────▼──────────────────────────────┐
│  Load Balancer (SLB)                   │
│  Distributes across servers             │
└──────────┬──────────────────────────────┘
           │
    ┌──────┴──────┐
    │             │
 ┌──▼──┐      ┌──▼──┐
 │Web1 │      │Web2 │  (Stateless)
 └──┬──┘      └──┬──┘
    │             │
    └──────┬──────┘
           │
    ┌──────▼──────────────┐
    │  Redis Cache        │
    │  (hot URLs)         │
    └──────┬──────────────┘
           │ (Cache miss)
    ┌──────▼──────────────┐
    │  RDS Database       │
    │  MySQL (replicated) │
    └─────────────────────┘

Async (via Message Queue):

┌──────────────────────────┐
│  Message Queue           │
│  (Click events)          │
└──────────┬───────────────┘
           │
     ┌─────▼─────┐
     │ Stats Job │  (Batch update statistics)
     └───────────┘

Part 3: Implementation

Step 1: Set Up Infrastructure (Terraform)

# terraform/main.tf

terraform {
  required_providers {
    alibabacloud = {
      source  = "aliyun/alibabacloud"
      version = "~> 1.0"
    }
  }
}

provider "alibabacloud" {
  region = "ap-southeast-1"
}

# VPC
resource "alibabacloud_vpc" "main" {
  name       = "url-shortener-vpc"
  cidr_block = "10.0.0.0/16"
}

# Public Subnet
resource "alibabacloud_vswitch" "public" {
  vpc_id            = alibabacloud_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-southeast-1a"
  name              = "public-subnet"
}

# Private Subnet
resource "alibabacloud_vswitch" "private" {
  vpc_id            = alibabacloud_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "ap-southeast-1b"
  name              = "private-subnet"
}

# Security Group
resource "alibabacloud_security_group" "web" {
  name        = "web-sg"
  vpc_id      = alibabacloud_vpc.main.id
  description = "Web servers"
}

resource "alibabacloud_security_group_rule" "http" {
  type              = "ingress"
  ip_protocol       = "tcp"
  port_range        = "80/80"
  cidr_ip           = "0.0.0.0/0"
  security_group_id = alibabacloud_security_group.web.id
}

resource "alibabacloud_security_group_rule" "https" {
  type              = "ingress"
  ip_protocol       = "tcp"
  port_range        = "443/443"
  cidr_ip           = "0.0.0.0/0"
  security_group_id = alibabacloud_security_group.web.id
}

# RDS Database
resource "alibabacloud_db_instance" "mysql" {
  engine               = "MySQL"
  engine_version       = "8.0"
  instance_type        = "mysql.n2.small.1"
  instance_storage     = 20
  instance_charge_type = "Postpaid"
  vswitch_id           = alibabacloud_vswitch.private.id
  security_group_ids   = [alibabacloud_security_group.web.id]
}

resource "alibabacloud_db_database" "main" {
  instance_id = alibabacloud_db_instance.mysql.id
  name        = "urlshortener"
}

# ElastiCache (Redis)
resource "alibabacloud_kvstore_instance" "redis" {
  instance_class = "redis.logic.sharding.1g.2db.0rw"
  instance_type  = "Redis"
  engine_version = "7.0"
  vswitch_id     = alibabacloud_vswitch.private.id
  security_group_ids = [alibabacloud_security_group.web.id]
}

# Auto Scaling Group
resource "alibabacloud_launch_template" "web" {
  image_id             = var.image_id
  instance_type        = "ecs.t6.medium"
  security_group_ids   = [alibabacloud_security_group.web.id]
  internet_max_bandwidth_out = "10"
  
  tag_specification {
    resource_type = "instance"
    tags = {
      Name = "url-shortener-web"
    }
  }
}

resource "alibabacloud_auto_scaling_group" "web" {
  min_size            = 2
  max_size            = 10
  desired_capacity    = 2
  launch_template_id  = alibabacloud_launch_template.web.id
  vswitch_ids         = [alibabacloud_vswitch.public.id]
}

# Load Balancer
resource "alibabacloud_slb" "main" {
  name              = "url-shortener-slb"
  internet_charge_type = "PayByTraffic"
  address_type      = "internet"
  spec              = "slb.s2.small"
}

resource "alibabacloud_slb_listener" "http" {
  load_balancer_id = alibabacloud_slb.main.id
  frontend_port    = 80
  backend_port     = 8080
  protocol         = "http"
  bandwidth        = 1024
}

# CDN
resource "alibabacloud_cdn_domain_new" "main" {
  domain_name = "short.example.com"
  cdn_type    = "web"
  scope       = "domestic"
  
  origin_servers = {
    origin_server_primary = alibabacloud_slb.main.address
  }
}

Step 2: Application Code (Node.js)

// src/server.js

const express = require('express');
const redis = require('redis');
const mysql = require('mysql2/promise');
const app = express();

// Initialize connections
const redisClient = redis.createClient();
const mysqlPool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// Middleware
app.use(express.json());

// POST /shorten - Create short URL
app.post('/shorten', async (req, res) => {
  try {
    const { longUrl, userId } = req.body;
    
    // Validate
    if (!longUrl || !isValidUrl(longUrl)) {
      return res.status(400).json({ error: 'Invalid URL' });
    }
    
    // Generate short code
    let shortCode;
    let exists = true;
    
    while (exists) {
      shortCode = generateShortCode();
      const [rows] = await mysqlPool.query(
        'SELECT * FROM urls WHERE short_code = ?', 
        [shortCode]
      );
      exists = rows.length > 0;
    }
    
    // Store in database
    await mysqlPool.query(
      'INSERT INTO urls (short_code, long_url, user_id, created_at) VALUES (?, ?, ?, NOW())',
      [shortCode, longUrl, userId]
    );
    
    // Cache for 24 hours
    await redisClient.setEx(
      `url:${shortCode}`,
      86400,
      JSON.stringify({ longUrl, shortCode, created_at: new Date() })
    );
    
    res.json({
      short_code: shortCode,
      short_url: `https://short.example.com/${shortCode}`,
      long_url: longUrl
    });
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// GET /:shortCode - Redirect
app.get('/:shortCode', async (req, res) => {
  try {
    const { shortCode } = req.params;
    
    // Check cache first
    let cached = await redisClient.get(`url:${shortCode}`);
    
    if (cached) {
      const data = JSON.parse(cached);
      // Async: Send click event to queue
      await sendToQueue('click-event', {
        shortCode,
        timestamp: new Date(),
        userAgent: req.headers['user-agent'],
        ip: req.ip
      });
      
      return res.redirect(302, data.longUrl);
    }
    
    // Cache miss: Query database
    const [rows] = await mysqlPool.query(
      'SELECT long_url FROM urls WHERE short_code = ? AND status = "active"',
      [shortCode]
    );
    
    if (rows.length === 0) {
      return res.status(404).json({ error: 'Short URL not found' });
    }
    
    const { long_url } = rows;
    
    // Update cache
    await redisClient.setEx(
      `url:${shortCode}`,
      86400,
      JSON.stringify({ longUrl: long_url, shortCode })
    );
    
    // Async: Send click event
    await sendToQueue('click-event', {
      shortCode,
      timestamp: new Date(),
      userAgent: req.headers['user-agent'],
      ip: req.ip
    });
    
    res.redirect(302, long_url);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// GET /stats/:shortCode - Get statistics
app.get('/stats/:shortCode', async (req, res) => {
  try {
    const { shortCode } = req.params;
    
    const [stats] = await mysqlPool.query(
      'SELECT visitor_count, unique_visitors, last_accessed_at FROM statistics WHERE short_code = ?',
      [shortCode]
    );
    
    if (stats.length === 0) {
      return res.json({ visitor_count: 0, unique_visitors: 0 });
    }
    
    res.json(stats);
  } catch (error) {
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Background job: Process click events
async function processClickEvents() {
  while (true) {
    try {
      // Get batch of events from queue
      const events = await getFromQueue('click-event', 100);
      
      if (events.length === 0) {
        await new Promise(resolve => setTimeout(resolve, 1000));
        continue;
      }
      
      // Group by short code
      const grouped = {};
      events.forEach(event => {
        if (!grouped[event.shortCode]) grouped[event.shortCode] = [];
        grouped[event.shortCode].push(event);
      });
      
      // Update database
      for (const [shortCode, events] of Object.entries(grouped)) {
        const count = events.length;
        const uniqueCount = new Set(events.map(e => e.ip)).size;
        
        await mysqlPool.query(
          `UPDATE statistics 
           SET visitor_count = visitor_count + ?, unique_visitors = ?, last_accessed_at = NOW()
           WHERE short_code = ?`,
          [count, uniqueCount, shortCode]
        );
      }
    } catch (error) {
      console.error('Error processing click events:', error);
    }
  }
}

// Utility functions
function generateShortCode(length = 6) {
  const charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  let code = '';
  for (let i = 0; i < length; i++) {
    code += charset[Math.floor(Math.random() * charset.length)];
  }
  return code;
}

function isValidUrl(string) {
  try {
    new URL(string);
    return true;
  } catch (_) {
    return false;
  }
}

async function sendToQueue(queueName, message) {
  // Implementation depends on message queue choice
  // (SQS, RabbitMQ, Alibaba Cloud Message Queue, etc.)
}

async function getFromQueue(queueName, limit) {
  // Implementation depends on message queue choice
}

// Start server
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
  // Start background job
  processClickEvents();
});

Part 4: Performance Optimization

Bottleneck 1: Database Queries

Problem: Every redirect requires a database query. At 100M redirects/day, that's 1,157 queries/second!

Solution: Redis cache

// Before: 1,157 db queries/second
const [rows] = await mysqlPool.query('SELECT ...');

// After: ~924 db queries/second (20% cache hit)
// Most requests hit Redis (in-memory, <1ms)
let cached = await redisClient.get(`url:${shortCode}`);
if (!cached) {
  const [rows] = await mysqlPool.query('SELECT ...');
}

Impact:

● Cache hit = <1ms latency

● Database query = 5-10ms latency

● Cost reduction: 80% fewer database queries

Bottleneck 2: Statistics Writing

Problem: Writing to database on every click degrades latency

Solution: Batch processing with message queue

// Before: Write to DB on every request
await mysqlPool.query('UPDATE statistics SET visitor_count = visitor_count + 1 ...');
// Adds 5-10ms to redirect latency

// After: Send event to queue (1ms), process asynchronously
await sendToQueue('click-event', { shortCode, ... });
// Adds <1ms to redirect latency

Impact:

● User-facing redirect latency reduced 80%

● Database write load smoothed out

● Statistics eventually consistent (acceptable for this use case)

Bottleneck 3: Global Distribution

Problem: Users in Europe take 200ms+ to reach Asia servers

Solution: CDN + Regional servers

# Deploy in multiple regions
resource "alibabacloud_slb" "sg" {
  name   = "url-shortener-sg"
  region = "ap-southeast-1"  # Singapore
}

resource "alibabacloud_slb" "de" {
  name   = "url-shortener-de"
  region = "eu-central-1"  # Frankfurt
}

Configure DNS to route users to nearest region (GeoDNS). CDN caches redirects globally.

Impact:

● P95 latency: 200ms → 50ms

● P99 latency: 500ms → 100ms

● Availability: 99.9% (region failure isolated)

Part 5: Cost Optimization

Current Cost (100M redirects/day)

Component Spec Monthly Cost
ECS (2-4 instances) t6.medium, auto-scaling $120
RDS db.t3.small, replicated $60
Redis 1GB cache $40
Load Balancer Standard SLB, 1M requests $50
CDN 100M requests $300
Data transfer Inter-region $100
Total ~$670/month

Cost per Million Redirects

$670 / 3,000M = $0.22 per million redirects

Optimization 1: Instance Right-Sizing

Current: t6.medium (2 vCPU, 4GB RAM) = $30/month each

Analysis: Actual usage

● CPU: 15% average, 40% peak

● Memory: 2GB average, 3GB peak

Optimize: t6.small (1 vCPU, 2GB RAM) = $15/month each

Savings: $15/month × 2 instances = $30/month (4.5%)

Optimization 2: RDS Read Replicas

Current: Single RDS db.t3.small ($60/month)

Problem: Analytics queries slow down production

Solution: Read replica ($40/month)

Benefits:

● Zero impact on production latency

● Analytics queries fast

● Total cost: +$40

Worth it? Yes—better insight into system.

Optimization 3: CDN Cache Optimization

Current: Cache everything, 24-hour TTL

Optimization: Smart TTL

● Popular URLs (>1000 clicks): 24-hour TTL

● Medium URLs (100-1000): 12-hour TTL

● Unpopular URLs (<100): 1-hour TTL

Cost impact: 15% fewer cache misses

Savings: $300 × 0.15 = $45/month (6.7%)

Optimization 4: Data Transfer

Current: Replicating between regions (expensive)

Optimization: Keep hot data in CDN, cold data in archive

Cost savings: $50/month (50%)

Total Optimizations

Optimization Savings
Instance right-sizing $30
Smart CDN TTL $45
Data transfer optimization $50
Total $125 (18.7%)

New monthly cost: $545 ($0.18 per million redirects)

Part 6: Monitoring

Key Metrics

Dashboards:

├─ System Health
│  ├─ CPU Utilization: 15% avg, 40% peak (target: <70%)
│  ├─ Memory: 2GB avg, 3GB peak (target: <80%)
│  ├─ Disk: 8GB used, 20GB available (target: <80%)
│  └─ Network: 100Mbps avg, 500Mbps peak
│
├─ Application
│  ├─ Redirect latency: P50=20ms, P95=45ms, P99=95ms
│  ├─ Error rate: 0.01% (target: <0.1%)
│  ├─ Shorten latency: P50=50ms, P95=100ms, P99=200ms
│  └─ Cache hit ratio: 80% (target: >75%)
│
├─ Database
│  ├─ Connections: 45/50 (target: <80%)
│  ├─ Query latency: P50=2ms, P95=8ms, P99=20ms
│  ├─ Replication lag: 0s (target: <1s)
│  └─ Slow queries: 0 (target: 0)
│
└─ Cost
   ├─ Daily spend: $18 (on pace for $540/month)
   ├─ Cost per million redirects: $0.18
   └─ Cost trend: -2% vs last month

Alerts

Trigger: CPU > 70% for 5 minutes
Action: Page on-call engineer, prepare to scale

Trigger: Cache hit ratio < 70%
Action: Investigate cache issues, potential memory pressure

Trigger: Error rate > 0.5%
Action: Page engineer, check logs

Trigger: Daily cost > $25
Action: Investigate cost anomalies

Part 7: Lessons Learned

What Worked Well

  1. Stateless design: Scaling was trivial—just add more instances
  2. Caching: Reduced database load by 80%
  3. Async processing: Improved user latency significantly
  4. Infrastructure as Code: Could recreate entire system in minutes
  5. Monitoring: Caught issues before users noticed

What to Do Differently

  1. Metrics planning: Wished I planned metrics earlier. Some insights required log analysis
  2. Testing: Should have load tested earlier. Caught bottleneck at 50M requests/day, not 100M
  3. Fallbacks: What happens if cache goes down? Needed failover plan
  4. Cost controls: Should have set budget alerts earlier

General Principles

✅ Cache everything cacheable

✅ Async everything non-critical

✅ Monitor early, often

✅ Right-size, don't over-provision

✅ Plan for failure

✅ Automate, don't click

Part 8: Next Steps for You

Level 1: Getting Started

● Clone the repository template

● Deploy infrastructure with Terraform

● Run application locally

● Create a few short URLs

Level 2: Production Ready

● Set up monitoring and alerts

● Configure CDN

● Implement cache layer

● Set up CI/CD pipeline

Level 3: Scale

● Optimize costs

● Deploy to multiple regions

● Advanced analytics

● Custom domain support

Level 4: Advanced

● GraphQL API

● Real-time collaboration

● ML-powered recommendations

● Mobile app

Wrapping Up

Congratulations! You now have:

Real project experience: You've designed and deployed a production-grade application

Performance optimization mindset: You identify bottlenecks and fix them systematically

Cost consciousness: You optimize not just for speed, but for efficiency

Full-stack knowledge: From infrastructure to code to monitoring

This series started with "getting started," and now you're ready to build for real.

Project Resources

Complete Code Repository

Terraform Templates

Node.js Best Practices

Redis Caching Strategies

Database Optimization

Final Words

Cloud computing isn't magic. It's:

Architecture: Thinking about how components interact

Automation: Removing manual steps and human error

Monitoring: Knowing what's happening in real-time

Optimization: Making systems faster and cheaper

You've learned all of these in this series.

The beautiful part? Everything you've learned applies to other projects too. The principles of scaling, security, and cost optimization are universal.

So take what you've learned here. Build your next project. Measure it. Optimize it. Share what you learn with others.

That's how we grow as engineers.

__

What will you build next? Comment below. I'd love to hear about your projects and help you troubleshoot along the way.

Series Complete! Thank you for joining me on this journey through Alibaba Cloud. Remember: The cloud is just someone else's computer—but configured really, really well.

Keep building. 🚀


Disclaimer: The views expressed herein are for reference only and don't necessarily represent the official views of Alibaba Cloud.

0 1 0
Share on

Farah Abdou

19 posts | 0 followers

You may also like

Comments