quicknotes/.cursor/rules/performance-optimization.mdc

345 lines
11 KiB
Text
Raw Permalink Normal View History

---
description: Standards for performance benchmarks and optimization strategies
globs: **/*.{ts,tsx,js,jsx,go,css,scss}
---
<rule>
filters:
- type: file_extension
pattern: "\\.(ts|tsx|js|jsx|go|css|scss)$"
- type: content
pattern: "(?s)performance|optimization|render|benchmark|profil|memory|cpu|network|latency|throughput"
actions:
- type: suggest
message: |
# Performance Optimization Standards
## Frontend Performance
1. Core Web Vitals Targets:
- Largest Contentful Paint (LCP): < 2.5s
- First Input Delay (FID): < 100ms
- Cumulative Layout Shift (CLS): < 0.1
- First Contentful Paint (FCP): < 1.5s
2. Bundle Size Targets:
- Initial JS bundle: < 170KB compressed
- Initial CSS: < 50KB compressed
- Total page size: < 1MB
- Use code splitting for routes
3. Rendering Optimization:
- React memo for pure components
- Virtual lists for long scrollable content
- Debounced/throttled event handlers
- useCallback/useMemo for expensive operations
4. React Component Guidelines:
- Move state up to appropriate level
- Use context API judiciously
- Avoid prop drilling
- Implement shouldComponentUpdate or memo
5. Asset Optimization:
- Optimize images (WebP/AVIF formats)
- Lazy load images and non-critical components
- Use font-display: swap
- Compress SVGs
## Backend Performance
1. API Response Time Targets:
- P50 (median): < 200ms
- P95: < 500ms
- P99: < 1000ms
2. Database Optimization:
- Use indexes for frequently queried fields
- Optimize queries with EXPLAIN
- Use database connection pooling
- Implement query caching for repeated requests
3. Go Performance:
- Use goroutines appropriately
- Avoid unnecessary allocations
- Profile with pprof
- Consider sync.Pool for frequent allocations
4. API Design:
- GraphQL for flexible data fetching
- Pagination for large result sets
- Partial responses
- Batch operations
5. Caching Strategy:
- Cache calculation results
- HTTP caching headers
- In-memory caching for frequent reads
- Distributed cache for shared data
## Network Optimization
1. HTTP/2 or HTTP/3 Support:
- Enable multiplexing
- Server push for critical resources
- Header compression
2. API Compression:
- Enable gzip/Brotli compression
- Compress responses > 1KB
- Skip compression for already compressed formats
3. CDN Usage:
- Static assets via CDN
- Edge caching for API responses
- Regional deployments for global users
## Monitoring and Benchmarking
1. Tools:
- Lighthouse for frontend performance
- New Relic/Datadog for backend monitoring
- Custom traces for critical paths
2. Load Testing:
- Benchmark on specification-matched environments
- Test at 2x expected peak load
- Identify bottlenecks
- Establish baseline and regression tests
3. Regular Performance Reviews:
- Weekly performance dashboard review
- Monthly deep-dive analysis
- Continuous monitoring alerts
## Performance Budgets
1. Regression Prevention:
- No more than 5% performance degradation between releases
- Alert on exceeding performance budgets
- Block deployment on critical performance failures
2. Optimization Targets:
- Identify top 3 performance issues each sprint
- Continuously improve critical user journeys
- Set specific targets for identified bottlenecks
## Implementation Guidelines
1. Performance First:
- Consider performance implications during design
- Profile before and after significant changes
- Document performance considerations in PRs
2. Known Patterns:
- Use established patterns for common performance issues
- Document performance tricks and techniques
- Share learnings across teams
3. Testing Environment:
- Test on low-end devices
- Test on slower network connections
- Test with representative datasets
metadata:
priority: high
version: 1.0
</rule>
examples:
- input: |
// Bad: Inefficient React component
function UserList({ users }) {
const [filter, setFilter] = useState('');
// Expensive operation on every render
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
type="text"
value={filter}
onChange={e => setFilter(e.target.value)}
/>
{filteredUsers.map(user => (
<div key={user.id}>
<img src={user.avatar} />
{user.name}
</div>
))}
</div>
);
}
output: |
// Good: Optimized React component
function UserList({ users }) {
const [filter, setFilter] = useState('');
// Memoized expensive operation
const filteredUsers = useMemo(() =>
users.filter(user =>
user.name.toLowerCase().includes(filter.toLowerCase())
),
[users, filter]
);
// Debounced filter change
const handleFilterChange = useCallback(
debounce(value => setFilter(value), 300),
[]
);
return (
<div>
<input
type="text"
defaultValue={filter}
onChange={e => handleFilterChange(e.target.value)}
/>
{filteredUsers.length > 100 ? (
<VirtualList
height={500}
itemCount={filteredUsers.length}
itemSize={50}
renderItem={({ index, style }) => {
const user = filteredUsers[index];
return (
<div key={user.id} style={style}>
<img
src={user.avatar}
loading="lazy"
width={40}
height={40}
alt={`${user.name}'s avatar`}
/>
{user.name}
</div>
);
}}
/>
) : (
filteredUsers.map(user => (
<div key={user.id}>
<img
src={user.avatar}
loading="lazy"
width={40}
height={40}
alt={`${user.name}'s avatar`}
/>
{user.name}
</div>
))
)}
</div>
);
}
- input: |
// Bad: Inefficient database query
func GetUserPosts(db *sql.DB, userID int) ([]Post, error) {
var posts []Post
rows, err := db.Query("SELECT * FROM posts WHERE user_id = ?", userID)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var post Post
err := rows.Scan(&post.ID, &post.Title, &post.Content, &post.UserID, &post.CreatedAt)
if err != nil {
return nil, err
}
// N+1 query problem
commentRows, err := db.Query("SELECT * FROM comments WHERE post_id = ?", post.ID)
if err != nil {
return nil, err
}
defer commentRows.Close()
for commentRows.Next() {
var comment Comment
err := commentRows.Scan(&comment.ID, &comment.Content, &comment.PostID, &comment.UserID)
if err != nil {
return nil, err
}
post.Comments = append(post.Comments, comment)
}
posts = append(posts, post)
}
return posts, nil
}
output: |
// Good: Optimized database query
func GetUserPosts(db *sql.DB, userID int) ([]Post, error) {
// First, fetch all posts in one query
var posts []Post
postRows, err := db.Query(`
SELECT id, title, content, user_id, created_at
FROM posts
WHERE user_id = ?
ORDER BY created_at DESC`,
userID)
if err != nil {
return nil, fmt.Errorf("error querying posts: %w", err)
}
defer postRows.Close()
postIDs := []int{}
postMap := make(map[int]*Post)
for postRows.Next() {
var post Post
err := postRows.Scan(&post.ID, &post.Title, &post.Content, &post.UserID, &post.CreatedAt)
if err != nil {
return nil, fmt.Errorf("error scanning post: %w", err)
}
posts = append(posts, post)
postIDs = append(postIDs, post.ID)
postMap[post.ID] = &posts[len(posts)-1]
}
if len(postIDs) == 0 {
return posts, nil
}
// Use a single query with IN clause to fetch all comments for all posts
query, args, err := sqlx.In(`
SELECT id, content, post_id, user_id
FROM comments
WHERE post_id IN (?)
ORDER BY created_at ASC`,
postIDs)
if err != nil {
return nil, fmt.Errorf("error preparing IN query: %w", err)
}
query = db.Rebind(query)
commentRows, err := db.Query(query, args...)
if err != nil {
return nil, fmt.Errorf("error querying comments: %w", err)
}
defer commentRows.Close()
// Populate the comments for each post
for commentRows.Next() {
var comment Comment
var postID int
err := commentRows.Scan(&comment.ID, &comment.Content, &postID, &comment.UserID)
if err != nil {
return nil, fmt.Errorf("error scanning comment: %w", err)
}
if post, ok := postMap[postID]; ok {
post.Comments = append(post.Comments, comment)
}
}
return posts, nil
}