All posts

Deep, practical engineering stories — browse everything.

Performance metrics dashboard illustration

Web App Performance Metrics and How to Measure Them

This article summarizes the DEV post “Key Performance Metrics for Web Apps and How to Measure Them,” focusing on the most important signals and how to capture them. Core metrics LCP (Largest Contentful Paint): loading speed; target < 2.5s p75. INP/TTI (Interaction to Next Paint / Time to Interactive): interactivity; target INP < 200ms p75. FCP (First Contentful Paint): first visual response. TTFB (Time to First Byte): server responsiveness. Bundle size & request count: total transferred bytes and requests. Measurement toolbox Lighthouse: automated audits; budgets and suggestions. WebPageTest: multi-location runs, filmstrips, waterfalls. RUM (GA or custom): real-user timing for live traffic. React profiler & perf tools: find slow renders/update frequency. Webpack/Vite bundle analyzer: visualize bundle composition and dead weight. Optimization reminders Ship less JS/CSS; enable tree shaking/code splitting. Compress and cache static assets; serve modern image formats. Trim request count; inline critical CSS for hero; defer non-critical JS. Watch layout stability; reserve space to avoid CLS hits. Set and enforce budgets (JS gz < 200KB, LCP < 2.5s p75, INP < 200ms). Takeaway: Track a small, high-signal set of metrics with both lab (Lighthouse/WPT) and field (RUM) data, then enforce budgets so regressions fail fast.

Read

Spring Boot Validation: Complete Guide with @Valid and @Validated

Validation is crucial in Spring Boot applications. Here’s a complete guide to using @Valid and @Validated. Basic Validation Dependencies <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> Bean Validation public class CreateUserRequest { @NotBlank(message = "Name is required") private String name; @Email(message = "Invalid email format") @NotBlank private String email; @Min(value = 18, message = "Age must be at least 18") @Max(value = 120, message = "Age must be at most 120") private Integer age; } @Valid vs @Validated @Valid @PostMapping("/users") public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) { // Validates request object } @Validated @RestController @Validated public class UserController { @GetMapping("/users/{id}") public User getUser(@PathVariable @Min(1) Long id) { // Validates path variable } } Custom Validators @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = PhoneNumberValidator.class) public @interface PhoneNumber { String message() default "Invalid phone number"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } public class PhoneNumberValidator implements ConstraintValidator<PhoneNumber, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { return value != null && value.matches("^\\+?[1-9]\\d{1,14}$"); } } Validation Groups public interface CreateGroup {} public interface UpdateGroup {} public class UserRequest { @NotNull(groups = UpdateGroup.class) private Long id; @NotBlank(groups = {CreateGroup.class, UpdateGroup.class}) private String name; } @PostMapping("/users") public ResponseEntity<User> create(@Validated(CreateGroup.class) @RequestBody UserRequest request) { // Only validates CreateGroup fields } Error Handling @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleValidation( MethodArgumentNotValidException ex) { Map<String, String> errors = new HashMap<>(); ex.getBindingResult().getFieldErrors().forEach(error -> errors.put(error.getField(), error.getDefaultMessage()) ); return ResponseEntity.badRequest() .body(new ErrorResponse("Validation failed", errors)); } } Best Practices Validate at controller level Use appropriate annotations Create custom validators Handle validation errors Use validation groups Conclusion Spring Boot validation provides: ...

Read
MongoDB performance optimization illustration

MongoDB Query Optimization: Finding Bottlenecks and Performance Tuning

MongoDB performance optimization requires a systematic approach to identify bottlenecks, analyze query patterns, and implement effective indexing strategies. This guide covers profiling techniques, index optimization, and load testing methodologies. Understanding MongoDB performance MongoDB performance depends on several factors: Query patterns: How queries are structured and what operations they perform Index usage: Whether queries can leverage indexes effectively Data volume: Size of collections and documents Hardware resources: CPU, memory, and disk I/O capacity Connection pooling: How applications manage database connections Profiling MongoDB queries Enable profiling MongoDB’s profiler collects detailed information about query execution: ...

Read

React Performance Optimization: Techniques and Best Practices

Optimizing React applications is crucial for better user experience. Here are proven techniques. 1. Memoization React.memo const ExpensiveComponent = React.memo(({ data }) => { return <div>{processData(data)}</div>; }, (prevProps, nextProps) => { return prevProps.data.id === nextProps.data.id; }); useMemo const expensiveValue = useMemo(() => { return computeExpensiveValue(a, b); }, [a, b]); useCallback const handleClick = useCallback(() => { doSomething(id); }, [id]); 2. Code Splitting React.lazy const LazyComponent = React.lazy(() => import('./LazyComponent')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent /> </Suspense> ); } 3. Virtualization import { FixedSizeList } from 'react-window'; function VirtualizedList({ items }) { return ( <FixedSizeList height={600} itemCount={items.length} itemSize={50} > {({ index, style }) => ( <div style={style}>{items[index]}</div> )} </FixedSizeList> ); } 4. Avoid Unnecessary Renders // Bad: Creates new object on every render <ChildComponent config={{ theme: 'dark' }} /> // Good: Use useMemo or constant const config = useMemo(() => ({ theme: 'dark' }), []); <ChildComponent config={config} /> Best Practices Memoize expensive computations Split code by routes Virtualize long lists Avoid inline functions/objects Use production builds Conclusion Optimize React apps for better performance! ⚡

Read

Vue 3 Composition API: Complete Guide and Best Practices

Vue 3’s Composition API provides better code organization. Here’s how to use it effectively. Setup import { ref, computed, watch } from 'vue'; export default { setup() { const count = ref(0); const doubled = computed(() => count.value * 2); watch(count, (newVal) => { console.log('Count changed:', newVal); }); return { count, doubled }; } } Script Setup <script setup> import { ref, computed } from 'vue'; const count = ref(0); const doubled = computed(() => count.value * 2); function increment() { count.value++; } </script> Composables // useCounter.js import { ref } from 'vue'; export function useCounter(initialValue = 0) { const count = ref(initialValue); const increment = () => count.value++; const decrement = () => count.value--; return { count, increment, decrement }; } Best Practices Use composables for reusability Keep setup functions focused Use script setup syntax Organize by feature Extract complex logic Conclusion Vue 3 Composition API enables better code organization! 🎯

Read

JavaScript Closures Explained: Understanding Scope and Memory

Closures are fundamental to JavaScript. Here’s how they work and when to use them. What is a Closure? A closure gives you access to an outer function’s scope from an inner function. function outer() { const name = 'JavaScript'; function inner() { console.log(name); // Accesses outer scope } return inner; } const innerFunc = outer(); innerFunc(); // "JavaScript" Common Patterns Module Pattern const counter = (function() { let count = 0; return { increment: () => ++count, decrement: () => --count, getCount: () => count }; })(); Function Factories function createMultiplier(multiplier) { return function(number) { return number * multiplier; }; } const double = createMultiplier(2); double(5); // 10 Common Pitfalls Loop with Closures // Bad: All log same value for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // 3, 3, 3 } // Good: Use let or IIFE for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // 0, 1, 2 } Best Practices Understand scope chain Avoid memory leaks Use closures for encapsulation Be careful in loops Use modern alternatives when possible Conclusion Master closures to write better JavaScript! 🔒

Read

CSS Grid vs Flexbox: When to Use Which

CSS Grid and Flexbox serve different purposes. Here’s when to use each. Flexbox Use for One-Dimensional Layouts .navbar { display: flex; justify-content: space-between; align-items: center; } Common Use Cases Navigation bars Centering content Flexible components Equal height columns CSS Grid Use for Two-Dimensional Layouts .container { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto; gap: 20px; } Common Use Cases Page layouts Complex grids Card layouts Responsive designs When to Use Both .container { display: grid; grid-template-columns: 1fr 3fr; } .sidebar { display: flex; flex-direction: column; } Best Practices Use Flexbox for components Use Grid for layouts Combine both when needed Consider browser support Test responsiveness Conclusion Choose the right tool for the job! 🎨

Read

Docker Best Practices: Building Efficient Images

Building efficient Docker images requires following best practices. Here’s how. 1. Use Multi-Stage Builds # Build stage FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Production stage FROM node:18-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules CMD ["node", "dist/index.js"] 2. Layer Caching # Bad: Changes invalidate cache COPY . . RUN npm install # Good: Dependencies cached COPY package*.json ./ RUN npm install COPY . . 3. Use .dockerignore node_modules .git .env dist *.log 4. Minimize Layers # Bad: Multiple layers RUN apt-get update RUN apt-get install -y python RUN apt-get install -y git # Good: Single layer RUN apt-get update && \ apt-get install -y python git && \ rm -rf /var/lib/apt/lists/* 5. Use Specific Tags # Bad: Latest tag FROM node:latest # Good: Specific version FROM node:18.17.0-alpine Best Practices Multi-stage builds Optimize layer order Use .dockerignore Minimize image size Security scanning Conclusion Build efficient Docker images! 🐳

Read

Redis Caching Strategies: Patterns and Best Practices

Redis is powerful for caching. Here are effective caching strategies. Cache-Aside Pattern async function getUser(id) { // Check cache let user = await redis.get(`user:${id}`); if (user) { return JSON.parse(user); } // Cache miss - fetch from DB user = await db.query('SELECT * FROM users WHERE id = ?', [id]); // Store in cache await redis.setex(`user:${id}`, 3600, JSON.stringify(user)); return user; } Write-Through Pattern async function updateUser(id, data) { // Update database const user = await db.update('users', id, data); // Update cache await redis.setex(`user:${id}`, 3600, JSON.stringify(user)); return user; } Cache Invalidation async function deleteUser(id) { // Delete from database await db.delete('users', id); // Invalidate cache await redis.del(`user:${id}`); } Best Practices Set appropriate TTL Handle cache misses Invalidate properly Monitor cache hit rate Use consistent key patterns Conclusion Implement effective Redis caching strategies! 🔴

Read

MongoDB Query Optimization: Indexing and Performance Tips

Optimizing MongoDB queries is essential for performance. Here’s how. Indexing Create Indexes // Single field index db.users.createIndex({ email: 1 }); // Compound index db.users.createIndex({ status: 1, createdAt: -1 }); // Text index db.posts.createIndex({ title: "text", content: "text" }); Explain Queries db.users.find({ email: "[email protected]" }).explain("executionStats"); Query Optimization Use Projection // Bad: Fetches all fields db.users.find({ status: "active" }); // Good: Only needed fields db.users.find( { status: "active" }, { name: 1, email: 1, _id: 0 } ); Limit Results db.users.find({ status: "active" }) .limit(10) .sort({ createdAt: -1 }); Best Practices Create appropriate indexes Use projection Limit result sets Avoid $regex without index Monitor slow queries Conclusion Optimize MongoDB for better performance! 🍃

Read