Capstone: Build a Feature End-to-End with AI
Apply everything you've learned. Build a complete feature from design to deployment using AI at every stage of the development lifecycle.
Premium Course Content
This lesson is part of a premium course. Upgrade to Pro to unlock all premium courses and content.
- Access all premium courses
- 1000+ AI skills included
- New content added weekly
Everything Comes Together
In the previous lesson, we explored architecture decisions and system design. Now let’s build on that foundation. Over the last seven lessons, you’ve learned to use AI for code generation, debugging, testing, code review, documentation, and architecture decisions. Now it’s time to use all of them on a single feature, from blank canvas to production-ready code.
This isn’t a toy exercise. We’re going to build a feature that a real application might need, and we’ll use AI at every stage of the process. You’ll see how the techniques compound—each stage is faster and more effective because of the AI-assisted stages before it.
The Feature: Rate Limiting API
We’re building a rate limiting system for an API. Here’s why this is a great capstone:
- It involves architecture decisions (where to store counters, which algorithm)
- It needs careful implementation (race conditions, edge cases)
- Testing is critical (timing, concurrency, boundary conditions)
- It needs clear documentation (other developers will configure it)
- Security implications require careful review
Requirements:
- Limit API requests per user per time window
- Support different limits for different endpoints
- Return standard rate limit headers (
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset) - Handle edge cases (clock skew, distributed servers, burst traffic)
- Configurable via middleware (easy to add to any route)
Stage 1: Architecture Decision (Lesson 7)
First, let’s decide how to build this. Here’s the prompt:
I need to add rate limiting to our Express.js/TypeScript
API. We use Redis already for caching.
Compare these rate limiting approaches for our situation:
1. Fixed window counter
2. Sliding window log
3. Sliding window counter
4. Token bucket
Our constraints:
- ~10,000 active users
- Need per-user, per-endpoint limits
- Running 3 API server instances behind a load balancer
- Redis available (already in our stack)
- Must handle 1,000 req/sec peak
For each approach, analyze: accuracy, Redis memory usage,
implementation complexity, and behavior at window boundaries.
AI lays out the trade-offs. For our case, it recommends the sliding window counter approach—it’s accurate enough (no hard boundary spikes like fixed window), memory-efficient (unlike sliding window log), and simpler than token bucket.
Your decision: Sliding window counter with Redis. Document it as an ADR.
Stage 2: Code Generation (Lesson 2)
Now generate the implementation. Notice how we provide our codebase context:
Implement a rate limiting middleware for Express.js
with TypeScript.
Algorithm: Sliding window counter using Redis
Storage: Our existing Redis instance (ioredis client)
Our middleware pattern:
[paste an existing middleware as example]
Requirements:
- Accepts config: { windowMs: number, maxRequests: number }
- Key format: ratelimit:{userId}:{endpoint}:{windowId}
- Returns 429 with Retry-After header when limit exceeded
- Adds X-RateLimit-* headers to all responses
- Handles Redis connection failures gracefully (fail open)
- TypeScript with strict types
Generate:
1. The RateLimitConfig interface
2. The middleware factory function
3. The Redis operations (increment, check, cleanup)
AI generates the middleware. Let’s say the core looks something like this:
interface RateLimitConfig {
windowMs: number;
maxRequests: number;
keyPrefix?: string;
failOpen?: boolean;
}
const createRateLimiter = (config: RateLimitConfig) => {
return async (req: Request, res: Response, next: NextFunction) => {
const userId = req.user?.id ?? req.ip;
const endpoint = req.route?.path ?? req.path;
const windowId = Math.floor(Date.now() / config.windowMs);
const prevWindowId = windowId - 1;
const currentKey = `ratelimit:${userId}:${endpoint}:${windowId}`;
const prevKey = `ratelimit:${userId}:${endpoint}:${prevWindowId}`;
try {
const [currentCount, prevCount] = await Promise.all([
redis.incr(currentKey),
redis.get(prevKey),
]);
// Set expiry on current window key
if (currentCount === 1) {
await redis.expire(currentKey, Math.ceil(config.windowMs / 1000) * 2);
}
// Sliding window calculation
const elapsed = (Date.now() % config.windowMs) / config.windowMs;
const weight = (Number(prevCount) || 0) * (1 - elapsed) + currentCount;
const remaining = Math.max(0, config.maxRequests - Math.ceil(weight));
const resetTime = (windowId + 1) * config.windowMs;
res.set({
'X-RateLimit-Limit': String(config.maxRequests),
'X-RateLimit-Remaining': String(remaining),
'X-RateLimit-Reset': String(Math.ceil(resetTime / 1000)),
});
if (remaining <= 0) {
const retryAfter = Math.ceil((resetTime - Date.now()) / 1000);
res.set('Retry-After', String(retryAfter));
return res.status(429).json({
error: 'Too many requests',
retryAfter,
});
}
next();
} catch (error) {
if (config.failOpen !== false) {
next(); // Fail open: allow request if Redis is down
} else {
res.status(503).json({ error: 'Service temporarily unavailable' });
}
}
};
};
Stage 3: Debugging (Lesson 3)
You run it locally and notice something odd: the rate limit count is slightly off when multiple requests arrive simultaneously. Time to debug:
I'm seeing a race condition in my rate limiter. When
10 concurrent requests arrive, the counter sometimes
shows 8 or 9 instead of 10.
Here's the code:
[paste the middleware]
The issue: redis.incr() and redis.get() are separate
commands. Between the INCR and GET, another request
could modify the counter.
How do I fix this race condition?
AI suggests using a Redis Lua script to make the increment-and-check atomic:
local current = redis.call('INCR', KEYS[1])
if current == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
local prev = redis.call('GET', KEYS[2]) or 0
return {current, prev}
Quick check: Before moving on, can you recall the key concept we just covered? Try to explain it in your own words before continuing.
This runs atomically on the Redis server—no race condition possible.
Stage 4: Testing (Lesson 4)
Now generate tests for the rate limiter:
Write comprehensive tests for this rate limiting middleware
using Jest and supertest.
[paste the final middleware code]
Test categories needed:
1. Basic rate limiting (requests within limit pass, over limit returns 429)
2. Sliding window behavior (requests in previous window affect current)
3. Header correctness (X-RateLimit-* headers have correct values)
4. Redis failure handling (fail open behavior)
5. Per-user isolation (user A's requests don't affect user B)
6. Per-endpoint isolation (calls to /api/users don't count against /api/posts)
7. Window reset behavior (limit resets after window passes)
8. Concurrent request handling
Use jest.useFakeTimers() for time-dependent tests.
Mock Redis for unit tests.
AI generates tests covering all these scenarios. Pay special attention to the concurrent request tests and the window boundary tests—these are the edge cases that break rate limiters in production.
Stage 5: Code Review (Lesson 5)
Before merging, run the code through AI review:
Review this rate limiting middleware for production readiness.
Focus on:
1. Security: Can users bypass the rate limit?
2. Performance: Any bottlenecks under load?
3. Reliability: What happens when things go wrong?
4. Correctness: Is the sliding window math right?
[paste the complete middleware code and tests]
AI might flag:
- “Users could bypass rate limiting by spoofing the
X-Forwarded-Forheader if you fall back toreq.ip. Ensure your proxy is configured to override, not append.” - “The Lua script should handle the case where KEYS[2] has expired. Currently
redis.call('GET')returnsfalse, not0—cast explicitly.” - “Consider adding a
skipfunction to the config so health check endpoints can bypass rate limiting.”
Each flag prevents a potential production issue.
Stage 6: Documentation (Lesson 6)
Finally, document the feature:
Generate documentation for our rate limiting middleware.
Audience: developers on our team who need to add rate
limiting to their endpoints.
Include:
1. Quick start (how to add rate limiting to a route)
2. Configuration options with defaults
3. How the sliding window algorithm works (brief)
4. Response headers explained
5. Error responses
6. Common configurations (strict for auth, relaxed for reads)
7. Troubleshooting guide
[paste the final middleware code]
AI produces documentation that lets any team member add rate limiting to their endpoints in minutes, without needing to understand the sliding window internals.
The Complete Workflow Summary
Here’s what we just did, and how long each stage takes with AI:
| Stage | Without AI | With AI | AI Contribution |
|---|---|---|---|
| Architecture decision | 2-4 hours research | 20 min | Trade-off analysis, option comparison |
| Code generation | 4-6 hours | 30-45 min | Initial implementation, boilerplate |
| Debugging | 1-2 hours (that race condition) | 15 min | Root cause analysis, atomic solution |
| Testing | 3-4 hours | 30-45 min | Comprehensive edge cases, test code |
| Code review | 30-60 min (waiting for reviewer) | 10 min | Security, performance, correctness checks |
| Documentation | 2-3 hours | 15-20 min | Structured docs from code |
Total without AI: 12-20 hours across multiple days Total with AI: 2-3 hours in a single session
That’s not 10x by cutting corners. Every stage is thorough—arguably more thorough than the non-AI version, because AI catches edge cases and security issues that humans skip under time pressure.
Your Challenge
Build a feature from your own project using this complete workflow:
- Choose a feature that involves real decisions (not just CRUD)
- Architecture: Use AI to explore options and document your decision
- Generate: Write code with AI using your codebase context
- Debug: When issues arise, use the debugging trifecta
- Test: Generate comprehensive tests including edge cases
- Review: Run an AI code review focused on security and performance
- Document: Create developer documentation from the final code
Time yourself. Compare to how long this would normally take. The difference is your AI development multiplier.
What You’ve Learned in This Course
| Lesson | Skill | Key Technique |
|---|---|---|
| 1 | AI-assisted development mindset | Context window management |
| 2 | Code generation | Three layers of context, incremental generation |
| 3 | Debugging | Debugging trifecta, five debugging patterns |
| 4 | Testing | Edge case generation, test review |
| 5 | Code review | Focused reviews, incremental refactoring |
| 6 | Documentation | Generating docs from code, keeping docs current |
| 7 | Architecture | Trade-off analysis, ADRs, “what am I not thinking about?” |
| 8 | End-to-end | Complete AI-assisted development workflow |
Key Takeaways
- The AI-assisted workflow touches every stage of development, not just code writing
- Each stage amplifies the next—good architecture decisions make code generation easier, good tests make review faster
- AI reduces a multi-day feature build to a few focused hours
- You own the decisions, AI accelerates the execution
- The best results come from combining your judgment with AI’s thoroughness
- Start using these techniques on real work—the learning curve is short and the payoff is immediate
Congratulations on completing Code Faster with AI. Go build something great, and let AI handle the parts that used to slow you down.
Knowledge Check
Complete the quiz above first
Lesson completed!