๐ Ultimate Challenge: Cloud Platform Capacity Limits & Security Defense
When you follow the previous tutorials and finally prepare to push your project to the cloud, you might suddenly encounter nightmares like "project too large, Git crashes" or "upload failure."
This isn't due to bugs in your codeโit's because you've hit the "hardware capacity ceiling" of major cloud platforms!
This chapter will outline the three most common landmines during deployment and distribution, and how professional engineers can skillfully avoid them!
๐ฃ Landmine #1: GitHub's 100MB File Size Ban
Scenario
When you want to package your work or source code into a ZIP file and include it in the project for clients to download. If you simply run zip -r my_project.zip . in the terminal, you'll find the file balloons to hundreds of MB or even over 1GB!
When you happily execute git push, the terminal mercilessly errors:
remote: error: GH001: Large files detected. You may want to try Git Large File Storage...
remote: error: File my_project.zip is 737.00 MB; this exceeds GitHub's file size limit of 100.00 MB
Why?
Because you've bundled all the "junk" and "history" from development:
node_modules: Contains tens of thousands of package files, easily exceeding 500MB..git: This hidden folder records every commitโthe older the history, the larger the file!.nextordist: These are compiled temporary files.
โ Solution: Write a Professional Exclusion Script
Stop manual packaging! Create a package_source_code.sh script in the project root, using zip's -x parameter to precisely filter unnecessary files, and replace secret .env keys with .env.example.
# package_source_code.sh example
OUTPUT_DIR="public/source_codes"
name="my_project"
# Delete old ZIP to prevent zip from incremental updates! (Critical!)
rm -f "$OUTPUT_DIR/${name}_source.zip"
zip -r -q "$OUTPUT_DIR/${name}_source.zip" . \
-x "node_modules/*" \
-x ".git/*" \
-x ".env" \
-x ".env.local" \
-x ".next/*" \
-x "dist/*" \
-x "public/source_codes/*" \
-x ".vercel/*" \
-x "*/.vercel/*" \
-x "*.jpg" -x "*/*.jpg" \
-x "*.png" -x "*/*.png"
echo "โ
Packaging complete!"
Result: A 700MB project shrinks to under 1MB! GitHub pushes will now succeed without issues!
๐ฃ Landmine #2: Supabase Free Tier's 50MB File Limit
Scenario
Some students avoid storing ZIPs on GitHub or Vercel (to save Vercel's deployment quota) and upload them to Supabase Storage instead.
Mid-upload, they encounter:
This project has a global file size limit of 50 MB.
Why?
Supabase's Free Tier is great but strictly limits single file uploads to 50MB to prevent abuse as free cloud storage. If your script shrinks the project to 1MB, this isn't a problem.
But what if your file genuinely exceeds 50MB (e.g., tutorial videos or high-res design drafts)?
โ Solution: Google Drive Invisible Redirect
Zero cost!
- Upload large files to Google Drive with "Anyone with the link can view" permissions.
- Keep a "Download" button in the frontend.
- Key: The button links to your backend API (e.g.,
/api/download), not Google Drive directly. - The backend API checks for login/payment and executes
return NextResponse.redirect("your_Google_Drive_url").
This bypasses the 50MB limit at no cost while maintaining basic access control!
๐ฃ Landmine #3: Hotlinking Risks with public Folder
Scenario
You wisely place the slimmed ZIP in Next.js's public/source_codes/ folder and add a frontend check: if (paid) { show download button }.
Why is this dangerous?
The public folder in Next.js equals publicly accessible static resources!
Even if the button is hidden, anyone guessing or obtaining the direct URL (e.g., https://your-domain.com/source_codes/my_project.zip) can download it without login or payment!
โ Solution: Supabase Private Bucket + Signed URLs
This is the commercial-grade protection knowledge platforms deserve:
- Create a private bucket: In Supabase Storage, create a
source-codesBucket set to Private (no public access). - Backend-issued temporary keys:
When users click download, call your backend/api/downloadAPI. After verifying permissions, use theService Role Keyto request a 60-second temporary download URL from Supabase.
// src/app/api/download/route.ts core defense logic
import { createClient as createSupabaseClient } from '@supabase/supabase-js';
// ... (Omitted: Verify user payment status) ...
// Use Service Role Key to bypass RLS
const supabaseAdmin = createSupabaseClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
// Generate a 60-second expiry download URL
const { data, error } = await supabaseAdmin
.storage
.from('source-codes')
.createSignedUrl(fileName, 60, {
download: fileName // Force browser download
});
return NextResponse.redirect(data.signedUrl);
With this ultimate protection, shared URLs become useless after 60 secondsโyour software monetization's strongest moat! ๐ฐ
Why Storage Limits Matter
Vercel imposes limits on serverless functions, files, and bandwidth. Exceeding them causes deployments to fail or applications to become unavailable. Understanding these limits helps you design applications that fit within platform constraints.
Why this matters for your career:
- Deployment failures from limit violations are embarrassing and avoidable
- Understanding platform limits helps you design within constraints
- Optimizing bundle size is a valuable frontend skill
- Knowing limits prevents production outages
Vercel Limits
| Resource | Hobby Plan | Pro Plan | |----------|-----------|----------| | Serverless function memory | 1024 MB | 3008 MB | | Serverless function timeout | 10s (Browser), 60s (Edge) | 60s (Browser), 900s (Edge) | | Serverless function size | 50 MB (zipped) | 50 MB (zipped) | | Bandwidth | 100 GB/month | 1 TB/month | | Build minutes | 6,000 min/month | 6,000 + extra | | Concurrent builds | 1 | 3 | | Team members | 1 | Unlimited | | Deployments per day | 100 | 1000 | |
Optimizing Bundle Size
| Technique | Reduction | Effort | |-----------|-----------|--------| | Code splitting | 30-60% | Low (automatic with Next.js) | | Tree shaking | 10-30% | Low (automatic with ES modules) | | Image optimization | 50-80% | Low (built-in with Next/Image) | | Dynamic imports | 20-50% | Medium | | Remove unused dependencies | 10-40% | Medium | | Use lighter alternatives | 20-60% | High (may need refactoring) |
Code Splitting with Next.js
// Before: all code loaded on every page
import { HeavyChartComponent } from '../components/HeavyChart';
// After: only loaded when needed
const HeavyChartComponent = dynamic(() => import('../components/HeavyChart'), {
loading: () => <p>Loading chart...</p>,
ssr: false // Don't render on server
});
Dynamic Imports
// Heavy library loaded only when user clicks
const handleClick = async () => {
const { default: confetti } = await import('canvas-confetti');
confetti();
};
Image Optimization
import Image from 'next/image';
// Next/Image optimizes automatically:
// - WebP conversion
// - Responsive sizes
// - Lazy loading
// - Blur placeholder
<Image
src="/campsite-photo.jpg"
alt="Sunset Ridge Campground"
width={800}
height={600}
priority={false} // Lazy load by default
/>
Serverless Function Packaging
// Minimize node_modules in functions
// Each function must be self-contained
// Bad: importing the entire AWS SDK
import { DynamoDB } from '@aws-sdk/client-dynamodb'; // ~50 KB
import { S3 } from '@aws-sdk/client-s3'; // ~50 KB
import { Lambda } from '@aws-sdk/client-lambda'; // ~30 KB
// Good: import only what you need
import { DynamoDB } from '@aws-sdk/client-dynamodb';
// Single service import: ~15 KB
Handling Large Files
// Vercel has a 50 MB limit on function bundles
// Strategies to stay under:
// 1. Move large assets to CDN (not in the bundle)
const imageUrl = 'https://cdn.myapp.com/images/sunset-ridge.jpg';
// 2. Use external CDN for heavy libraries
// Instead of bundling Chart.js, load from CDN
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
// 3. Tree-shake unused code
// Instead of: import lodash
// Use: import debounce from 'lodash/debounce'
// 4. Move compute to external API
// Instead of bundling a PDF generator in the function
const pdfUrl = await fetch('https://api.pdfservice.com/generate', {
method: 'POST',
body: JSON.stringify(htmlContent)
});
Summary
Understanding Vercel's limits helps you design applications that deploy reliably. Optimize bundle size with code splitting, tree shaking, and dynamic imports. Move heavy assets to CDN and import only what you need in serverless functions.
Key takeaways:
- Hobby: 1024 MB memory, 10s timeout, 50 MB function size
- Pro: 3008 MB memory, 60s timeout, 50 MB function size
- Code splitting with dynamic() reduces bundle size 30-60%
- Dynamic imports load heavy libraries only when needed
- Next/Image optimizes images automatically (WebP, lazy, responsive)
- Import only individual services from AWS SDK (not the entire SDK)
- Move large assets to CDN instead of bundling
- Tree-shake by importing specific modules (not entire libraries)
What's Next: GitHub Basics
The next course covers GitHub basics โ repositories, branches, pull requests, and team collaboration workflows.