Learning Roadmaps
Structured paths from Beginner to Advanced
Step-by-step roadmaps to help software engineers build real-world skills in Frontend, Backend and DevOps — with recommended tools, topics and tutorials at every stage.
A structured path covering markup, styling, JavaScript, TypeScript, React, tooling, and deployment — the way modern product teams ship UIs.
Open full page →- 1Beginner
Web Foundations
Learn how the browser renders pages and how to write semantic, accessible markup with modern CSS.
1
HTML5 fundamentals
Document structure, semantic tags, forms and accessibility landmarks.
Overview
HTML is the skeleton of every web page. It defines the structure and meaning of content using elements like `header`, `nav`, `main`, `article` and `footer`. Browsers, search engines and assistive technology all rely on this structure to understand a page.
In real-world software engineering, semantic HTML is not optional — it directly impacts SEO, accessibility compliance (WCAG) and how easily other developers can maintain the code. Most production frontend codebases enforce semantic markup through linting rules and code review.
Start by building small static pages by hand: a personal bio, a product page, a contact form. This is where you build the muscle memory you'll lean on for the rest of your career.
Learning objectives
- Understand the difference between semantic and non-semantic elements
- Build accessible forms with proper labels, fieldsets and validation attributes
- Use landmark elements and ARIA only when semantic HTML isn't enough
- Ship a multi-page static website with valid, accessible markup
Practical examples
- Personal portfolio with hero, projects and contact sections
- Restaurant landing page with semantic menu and reservation form
- Blog index page that lists articles using `<article>` cards
Recommended tools
- VS Code
Free editor with great HTML/CSS/JS tooling out of the box.
- Chrome DevTools
Inspect the DOM, accessibility tree and live-edit styles.
- axe DevTools
Automated accessibility audits inside the browser.
- MDN Web Docs
Authoritative reference for every HTML element and attribute.
Code examples
A semantic article cardhtml <article class="card"> <header> <h2>Getting started with HTML</h2> <p><time datetime="2026-05-01">May 1, 2026</time></p> </header> <p>HTML gives your content structure and meaning…</p> <a href="/blog/html-basics">Read more</a> </article>Common pitfalls
- Using `<div>` and `<span>` for everything instead of semantic elements
- Forgetting `<label>` on form inputs (breaks accessibility and autofill)
- Skipping heading levels (jumping from `h1` straight to `h4`)
2
CSS & responsive design
Box model, Flexbox, Grid, custom properties and mobile-first layouts.
Overview
CSS controls how your markup looks and adapts to every screen. The fundamentals are the box model (margin, border, padding, content), the cascade and specificity, and modern layout systems: Flexbox for one-dimensional layouts and CSS Grid for two-dimensional ones.
Responsive design is a baseline expectation in production. Mobile traffic is typically 50–70% of visits, so layouts must work on a 360px phone before they work on a 1440px desktop. Mobile-first media queries, fluid typography with `clamp()`, and container queries are the modern toolkit.
CSS custom properties (variables) are what let you build a small, reusable design system — colors, spacing, radii — that scales across pages without fighting the cascade.
Learning objectives
- Lay out any UI with Flexbox and CSS Grid without reaching for hacks
- Build mobile-first responsive pages using `min-width` media queries
- Define a token-based design system with CSS custom properties
- Debug layout issues using the browser's DevTools layout inspector
Practical examples
- Pricing page with three responsive plan cards
- Dashboard layout with a sidebar, header and content grid
- Hero section with fluid typography that scales from phone to desktop
Recommended tools
- Chrome DevTools
Visualize the box model, Grid and Flex layouts live.
- Tailwind CSS
Industry-standard utility-first framework used at most product companies.
- Can I Use
Check browser support before adopting new CSS features.
Code examples
Responsive card grid with CSS Gridcss .card-grid { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); } @media (min-width: 768px) { .card-grid { gap: 2rem; } }Common pitfalls
- Designing desktop-first and trying to retrofit mobile later
- Using fixed pixel widths that break at smaller screens
- Overusing `!important` instead of fixing specificity
3
JavaScript essentials
Variables, functions, arrays, objects, async/await, DOM APIs and fetch.
Overview
JavaScript is what makes pages interactive. It runs in the browser, in Node.js servers and even in edge functions. Before reaching for a framework, you need solid fundamentals: values and types, functions and closures, arrays and objects, modules, and the asynchronous model with promises and `async/await`.
The DOM API (`document.querySelector`, `addEventListener`) is how vanilla JavaScript manipulates the page. `fetch()` is how you talk to APIs. Build a few projects without React — a todo app, a weather widget, an image gallery — and you'll understand exactly what frameworks abstract for you later.
In production, JavaScript powers every interaction: form validation, search-as-you-type, animations, analytics, single-page navigation and more.
Learning objectives
- Read and write modern ES2020+ syntax (let/const, arrow funcs, destructuring, spread)
- Handle async work with promises and `async/await` correctly
- Manipulate the DOM and respond to events without a framework
- Call REST APIs with `fetch()` and handle errors gracefully
Practical examples
- Todo app with localStorage persistence
- Weather widget that calls a public API
- Image carousel with keyboard and swipe controls
Recommended tools
- Node.js
Run JavaScript outside the browser and use npm packages.
- MDN Web Docs
Best reference for every JavaScript API.
- ESLint
Catch common bugs and enforce a consistent code style.
Code examples
Fetching JSON with async/await and error handlingjavascript async function getUser(id) { try { const res = await fetch(`/api/users/${id}`); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); } catch (err) { console.error("Failed to load user", err); return null; } }Common pitfalls
- Mixing up `==` and `===` (always prefer strict equality)
- Forgetting to `await` an async call — silent bugs follow
- Mutating React/state objects directly instead of returning new ones
Stage tools:VS CodeChrome DevToolsMDN Web DocsCan I Use - 2Intermediate
TypeScript & React
Move from vanilla JS to a typed component model with React hooks and predictable state.
1
TypeScript
Types, interfaces, generics and strict mode for safer refactors.
Overview
TypeScript adds a static type layer on top of JavaScript so the compiler catches bugs before users do. It's used at almost every modern product company because it dramatically improves refactoring safety and editor autocomplete.
Start with primitives and interfaces for object shapes, then learn unions, generics and the built-in utility types (`Partial`, `Pick`, `Omit`, `Record`). Always enable `strict` mode from day one — it forces good habits and produces a much safer codebase.
In production, TypeScript is what lets a team of 10+ engineers safely rename a function, change an API contract or upgrade a library without breaking the app.
Learning objectives
- Write typed functions, interfaces and discriminated unions
- Use generics to build reusable, type-safe utilities
- Configure `tsconfig.json` with strict mode enabled
- Read and fix TypeScript compiler errors with confidence
Practical examples
- Typed API client wrapping `fetch()` with Zod validation
- Reusable `useLocalStorage<T>()` generic hook
- Discriminated union for a request state (`idle | loading | success | error`)
Recommended tools
- TypeScript
The language itself — install via npm and run `tsc`.
- VS Code
Best-in-class TypeScript integration with inline errors and refactors.
- Zod
Runtime validation that produces TypeScript types automatically.
Code examples
A generic, typed fetchertypescript export async function getJson<T>(url: string): Promise<T> { const res = await fetch(url); if (!res.ok) throw new Error(`HTTP ${res.status}`); return (await res.json()) as T; } type User = { id: string; name: string }; const user = await getJson<User>("/api/users/1");Common pitfalls
- Using `any` to silence the compiler instead of modeling the type
- Trusting external data without runtime validation (use Zod)
- Skipping `strict` mode — most of TypeScript's value lives there
2
React fundamentals
Components, props, JSX, conditional rendering and lists.
Overview
React lets you describe UI as a function of state. You build small, reusable components and compose them into pages. JSX is just sugar for `React.createElement` calls and compiles to plain JavaScript.
Focus on the core mental model first: components receive props, render JSX, and re-render when state changes. Composition (passing components as children or props) is what makes React codebases scale.
React powers Facebook, Instagram, Netflix, Airbnb and most modern dashboards — it is the most in-demand frontend skill in the job market today.
Learning objectives
- Build reusable components with typed props
- Render conditional UI and lists with stable `key`s
- Lift state up and pass callbacks down for child interactions
- Compose components using `children` and slot patterns
Practical examples
- Card component reused across blog index and search results
- Tabs component with controlled selected state
- Form built from small `<Field>` and `<Button>` primitives
Recommended tools
- React 19
The framework. Use Vite to scaffold a new project quickly.
- React DevTools
Inspect component trees, props and state in the browser.
- Storybook
Develop and document components in isolation.
Code examples
A typed, reusable Button componenttsx type Props = { variant?: "primary" | "ghost"; children: React.ReactNode; onClick?: () => void; }; export function Button({ variant = "primary", children, onClick }: Props) { return ( <button onClick={onClick} className={variant === "primary" ? "btn-primary" : "btn-ghost"} > {children} </button> ); }Common pitfalls
- Using array index as `key` for dynamic lists (breaks reconciliation)
- Mutating props or state instead of returning new objects
- Building one giant component instead of composing small ones
3
React hooks & state management
useState, useEffect, useMemo, custom hooks and shared state patterns.
Overview
Hooks let function components own state and side effects. Master the core ones first: `useState` for local state, `useEffect` for side effects, `useMemo` and `useCallback` for performance, and `useRef` for mutable values that don't trigger re-renders.
Once comfortable, extract repeated logic into custom hooks — that one pattern is what keeps real-world React codebases maintainable.
For shared state: keep UI state local, lift it up when siblings need it, use Context for low-frequency global values (theme, user), and reach for a dedicated store like Zustand or Redux Toolkit only when you truly have frequently-updated shared state.
Learning objectives
- Use the core hooks correctly and follow the rules of hooks
- Write custom hooks that encapsulate fetching, forms or subscriptions
- Choose the right state scope: local, lifted, context or store
- Avoid unnecessary re-renders with memoization where it actually matters
Practical examples
- `useDebounce()` custom hook for a search input
- Theme context with light/dark toggle persisted to localStorage
- Cart store in Zustand shared across navbar, product page and checkout
Recommended tools
- Zustand
Minimal, hook-based global state — great default for product apps.
- Redux Toolkit
Battle-tested store with devtools for large enterprise codebases.
- React DevTools Profiler
Measure re-renders to find real performance issues.
Code examples
A custom useDebounce hooktsx import { useEffect, useState } from "react"; export function useDebounce<T>(value: T, delay = 300): T { const [debounced, setDebounced] = useState(value); useEffect(() => { const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }Common pitfalls
- Putting everything in global state instead of keeping it local
- Forgetting dependency arrays in `useEffect` (causes stale closures)
- Premature memoization — `useMemo` everywhere makes code harder to read
4
API integration
Fetching, caching with TanStack Query, error handling and optimistic UI.
Overview
Almost every real app talks to a backend. Start with plain `fetch()` to understand requests and responses, then graduate to TanStack Query for caching, deduplication and background refetching — it eliminates 80% of the data-fetching boilerplate you'd write by hand.
Always model loading and error states explicitly. A blank screen during a slow request is one of the most common UX mistakes. Validate server responses with Zod so a bad payload can't crash your UI.
For write operations, add optimistic updates so the UI feels instant, and roll back on failure.
Learning objectives
- Fetch data with TanStack Query and handle loading/error UI
- Validate API responses with Zod before using them
- Implement optimistic updates for create/update/delete mutations
- Cancel in-flight requests when components unmount
Practical examples
- Paginated table backed by a REST API
- Search-as-you-type with debounced query and caching
- Optimistic 'like' button on a social feed
Recommended tools
- TanStack Query
De-facto data fetching layer for React apps.
- Zod
Runtime validation that doubles as TypeScript types.
- Postman
Explore and test REST APIs before wiring them into the UI.
Code examples
Fetching a list with TanStack Querytsx import { useQuery } from "@tanstack/react-query"; import { z } from "zod"; const Post = z.object({ id: z.string(), title: z.string() }); export function usePosts() { return useQuery({ queryKey: ["posts"], queryFn: async () => { const res = await fetch("/api/posts"); const data = await res.json(); return z.array(Post).parse(data); }, }); }Common pitfalls
- Refetching the same data on every render instead of caching
- Not handling the error state — users see a blank screen
- Trusting API responses without validation
Related tutorials on MasterLabLearn
Stage tools:TypeScriptReact 19TanStack QueryZod - 3Advanced
Tooling, Testing & Production
Ship fast, accessible, observable frontends with modern build tools and hosting.
1
Build tools (Vite & Webpack)
Bundlers, dev servers, code-splitting, tree-shaking and bundle analysis.
Overview
Modern bundlers turn your source files into optimized assets the browser can load fast. Vite is the modern default — it uses native ES modules in dev for near-instant startup and Rollup for production builds. Webpack is still common in older codebases and gives you finer control.
Learn how code-splitting and dynamic `import()` keep the initial bundle small, how tree-shaking removes unused exports, and how to read a bundle analyzer report to find what's making your app heavy.
In production, a 200KB-vs-2MB bundle is the difference between a snappy app and one users abandon.
Learning objectives
- Scaffold a new React + TypeScript app with Vite
- Configure environment variables and path aliases
- Code-split routes with dynamic `import()` and `React.lazy`
- Analyze and shrink your production bundle
Practical examples
- Vite + React + Tailwind starter for a SaaS dashboard
- Route-level code splitting in a multi-page app
- Bundle analyzer report identifying a 400KB rogue dependency
Recommended tools
- Vite
Fast modern bundler — the default for new React projects.
- rollup-plugin-visualizer
Generate an interactive bundle treemap.
- esbuild
Underlying transformer Vite uses for blazing-fast builds.
Code examples
Route-level code splitting with React.lazytsx import { lazy, Suspense } from "react"; const Settings = lazy(() => import("./Settings")); export function App() { return ( <Suspense fallback={<p>Loading…</p>}> <Settings /> </Suspense> ); }Common pitfalls
- Importing huge libraries (moment, lodash) for one helper
- Shipping source maps publicly to production
- Forgetting `import.meta.env` variables must be prefixed with `VITE_`
2
Frontend testing
Vitest, React Testing Library and Playwright for end-to-end coverage.
Overview
A healthy test pyramid has many fast unit tests, a smaller layer of component tests and a few critical end-to-end flows. Vitest is the modern test runner (Jest-compatible API, Vite-native speed). React Testing Library encourages testing what the user sees, not implementation details.
Playwright drives a real browser for end-to-end coverage — sign-up, checkout, login flows. Run unit and component tests on every push; run e2e on every PR before merging.
A solid test suite is what lets a team refactor confidently and ship multiple times per day.
Learning objectives
- Write unit tests with Vitest for pure functions and hooks
- Test components by interacting with them like a user would
- Author Playwright e2e tests for the most critical user journeys
- Wire all three layers into CI with coverage reports
Practical examples
- Unit tests for a `formatPrice()` utility
- Component test for a login form with validation errors
- Playwright test that signs up, logs in and creates a project
Recommended tools
- Vitest
Vite-native test runner, drop-in Jest replacement.
- React Testing Library
Encourages user-centric component tests.
- Playwright
Cross-browser end-to-end testing with great DX.
Code examples
A React Testing Library component testtsx import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { Counter } from "./Counter"; test("increments when clicked", async () => { render(<Counter />); await userEvent.click(screen.getByRole("button", { name: /add/i })); expect(screen.getByText("Count: 1")).toBeInTheDocument(); });Common pitfalls
- Testing implementation details (state shape) instead of behavior
- No tests at all because 'frontend changes too fast'
- Slow e2e suite that nobody runs locally
Related tutorials on MasterLabLearn
3
Deployment (Netlify, Vercel & Cloudflare)
Preview deploys, environment variables, custom domains and CI checks.
Overview
Modern frontend hosts deploy a preview for every pull request, which makes design and QA reviews dramatically easier. Netlify, Vercel and Cloudflare Pages all support this out of the box with zero infrastructure to manage.
Wire up CI to run type checks, linting and tests on every push, then auto-deploy `main` to production. Configure environment variables per environment, add a custom domain with automatic HTTPS, and enable analytics or RUM.
This is the modern frontend deployment pipeline — fast, safe and almost free for small projects.
Learning objectives
- Connect a Git repo to a host and configure build settings
- Set per-environment variables (production vs preview)
- Add a custom domain with automatic HTTPS
- Enable preview deploys and require CI checks before merge
Practical examples
- Deploy a Vite + React app to Vercel with `vercel.json` config
- Cloudflare Pages site with a custom domain and Web Analytics
- GitHub Actions workflow that blocks merge if tests fail
Recommended tools
- Vercel
First-class DX for React/Next.js with edge functions.
- Netlify
Great for static sites and JAMstack workflows.
- Cloudflare Pages
Generous free tier and a global edge network.
Code examples
GitHub Actions: typecheck, lint and build on every PRyaml name: CI on: [pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: 20 } - run: npm ci - run: npm run typecheck - run: npm run lint - run: npm run buildCommon pitfalls
- Committing `.env` files with production secrets
- Not enabling branch protection — broken code reaches `main`
- Hard-coding API URLs instead of using environment variables
Related tutorials on MasterLabLearn
Stage tools:ViteVitestPlaywrightVercelNetlify
A production-focused path from Java fundamentals to microservices, databases, security and system design.
Open full page →- 1Beginner
Language & API Fundamentals
Master the language, HTTP and the request/response lifecycle before reaching for frameworks.
1
Java fundamentals
Syntax, types, control flow, collections, streams and exception handling.
Overview
Before touching Spring, get fluent in Java itself. Learn the type system, control flow, classes and interfaces, collections (`List`, `Map`, `Set`) and the Streams API for functional data transformations. Understand checked vs unchecked exceptions and when to throw which.
Get comfortable with the JVM as well — heap vs stack, garbage collection basics, and how the JIT compiler optimizes hot code. This understanding explains most production performance issues you'll see later.
Java 21 is a great target: it has records, pattern matching, virtual threads and a much smaller boilerplate footprint than older versions.
Learning objectives
- Read and write idiomatic modern Java (records, var, switch expressions)
- Use collections and streams to manipulate data fluently
- Handle exceptions correctly with try/catch/finally and try-with-resources
- Understand basic JVM behavior (heap, GC, threads)
Practical examples
- CLI tool that reads a CSV and prints aggregated stats
- Bank account class with deposit/withdraw and overdraft rules
- Word-count program using the Streams API
Recommended tools
- Java 21 (Temurin)
Free, production-ready OpenJDK distribution.
- IntelliJ IDEA
Best-in-class Java IDE with deep refactoring support.
- Maven
Most common Java build tool and dependency manager.
Code examples
Modern Java with records and streamsjava record Order(String id, double amount) {} double totalAbove(List<Order> orders, double min) { return orders.stream() .filter(o -> o.amount() > min) .mapToDouble(Order::amount) .sum(); }Common pitfalls
- Catching `Exception` and ignoring it (swallowed bugs)
- Using raw types instead of generics
- Mutating shared collections from multiple threads without synchronization
2
Object-oriented programming
Classes, interfaces, inheritance, composition and SOLID principles.
Overview
OOP is the dominant paradigm in Java. The four pillars — encapsulation, inheritance, polymorphism and abstraction — show up in every codebase. Just as important are the SOLID principles, which guide you toward designs that are easy to change.
In practice, prefer composition over inheritance. Deep class hierarchies are a known anti-pattern; small interfaces and dependency injection produce code that's far easier to test and maintain.
This is the foundation that makes Spring Boot make sense later — Spring is essentially an opinionated, productive way to apply these principles at scale.
Learning objectives
- Model a domain with classes, interfaces and enums
- Apply SOLID principles in real refactoring exercises
- Choose composition over inheritance when designing new code
- Write small, focused classes with single responsibilities
Practical examples
- Refactor a god-class into focused services
- Strategy pattern for swappable payment providers
- Notification system using composition (email, SMS, push)
Recommended tools
- IntelliJ IDEA
Automated refactoring for extract method/class/interface.
- PlantUML
Quickly sketch class diagrams for design discussions.
Code examples
Strategy pattern with an interfacejava interface PaymentProvider { void charge(String userId, double amount); } class StripeProvider implements PaymentProvider { public void charge(String userId, double amount) { /* ... */ } } class CheckoutService { private final PaymentProvider provider; CheckoutService(PaymentProvider provider) { this.provider = provider; } void pay(String userId, double amount) { provider.charge(userId, amount); } }Common pitfalls
- Deep inheritance hierarchies that are hard to change
- Public mutable fields instead of private + accessors
- Putting business logic in entity classes instead of services
3
HTTP & REST API design
Verbs, status codes, idempotency, resource modeling and pagination.
Overview
Every backend speaks HTTP. Learn the verbs (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`), the difference between safe and idempotent requests, and what status codes actually mean (`200`, `201`, `204`, `400`, `401`, `403`, `404`, `409`, `422`, `500`).
Practice modeling resources as URLs (`/users/{id}/orders`), designing pagination (`?page=2&size=20` or cursor-based), filtering, and error response shapes that frontend developers will actually enjoy consuming.
A well-designed REST API is self-documenting and forgiving — bad ones generate endless support tickets.
Learning objectives
- Design RESTful resource URLs and choose correct HTTP verbs
- Use status codes correctly and return consistent error shapes
- Implement pagination, filtering and sorting
- Document APIs with OpenAPI/Swagger
Practical examples
- User CRUD endpoints with proper status codes
- Cursor-paginated feed endpoint
- Bulk import endpoint that returns per-row errors
Recommended tools
- Postman
Explore, test and document REST APIs interactively.
- Insomnia
Lightweight alternative to Postman.
- Swagger / OpenAPI
Generate interactive API docs from annotations.
Code examples
A well-formed REST exchangehttp POST /api/orders HTTP/1.1 Content-Type: application/json { "productId": "p_42", "quantity": 2 } HTTP/1.1 201 Created Location: /api/orders/o_1001 Content-Type: application/json { "id": "o_1001", "status": "pending" }Common pitfalls
- Returning `200 OK` for everything (including errors)
- Putting verbs in URLs (`/getUser?id=1`) instead of using HTTP verbs
- Inconsistent error payloads across endpoints
Related tutorials on MasterLabLearn
Stage tools:Java 21MavenIntelliJ IDEAPostman - 2Intermediate
Spring Boot, Data & Security
Wire up controllers, services and repositories backed by a real relational database — secured properly.
1
Spring Boot fundamentals
Controllers, services, repositories, dependency injection and configuration.
Overview
Spring Boot is the dominant Java backend framework. It gives you a productive, opinionated starting point: auto-configuration, an embedded server (Tomcat), starters for every common need, and a layered architecture (`@Controller` → `@Service` → `@Repository`).
Understand how dependency injection wires beans together via constructor injection, how `application.yml` and Spring profiles handle per-environment configuration, and how Spring Boot starters bring in entire stacks (web, data, security) with a single dependency.
This is the backbone of most enterprise Java backends today, from startups to Fortune 500s.
Learning objectives
- Build a `@RestController` with proper request/response DTOs
- Inject services using constructor injection (no field injection)
- Externalize configuration with `application.yml` and profiles
- Use Spring's auto-configuration without fighting it
Practical examples
- Tasks API with full CRUD endpoints
- Configuration overrides for `dev`, `staging` and `prod` profiles
- Health and info endpoints exposed via Spring Boot Actuator
Recommended tools
- Spring Initializr
Scaffold a Spring Boot project in seconds.
- Spring Boot DevTools
Hot reload during development.
- Lombok
Reduce boilerplate (use judiciously).
Code examples
A minimal Spring Boot REST controllerjava @RestController @RequestMapping("/api/tasks") public class TaskController { private final TaskService service; public TaskController(TaskService service) { this.service = service; } @GetMapping public List<TaskDto> list() { return service.findAll(); } @PostMapping public ResponseEntity<TaskDto> create(@Valid @RequestBody CreateTaskDto body) { TaskDto created = service.create(body); return ResponseEntity.created(URI.create("/api/tasks/" + created.id())).body(created); } }Common pitfalls
- Field injection (`@Autowired` on fields) — breaks testing
- Putting JPA entities directly in API responses (couples API to DB)
- Catching and rethrowing exceptions without context
Related tutorials on MasterLabLearn
2
Data persistence (SQL, JPA & Hibernate)
SQL fundamentals, JPA/Hibernate mapping, migrations and connection pooling.
Overview
Most bugs in real systems live at the database boundary. Learn SQL first — joins, indexes, transactions, isolation levels — before reaching for an ORM. Then use JPA/Hibernate to map entities, write JPQL queries and master the entity lifecycle.
Version your schema with Flyway or Liquibase so every environment matches and every change is reviewable. Configure HikariCP (Spring Boot's default pool) sensibly — connection pooling is the difference between a service that scales and one that falls over.
For most applications, PostgreSQL is the right default: powerful, reliable, free.
Learning objectives
- Write SQL queries with joins, aggregates and window functions
- Map entities and relationships with JPA annotations
- Manage schema with Flyway migrations
- Tune HikariCP and explain N+1 query problems
Practical examples
- Blog schema with `users`, `posts`, `comments` and proper indexes
- Flyway migration that adds a column with a backfill
- Repository query that avoids N+1 with `@EntityGraph`
Recommended tools
- PostgreSQL
Best-in-class open-source relational database.
- Flyway
Versioned, repeatable database migrations.
- DBeaver
Free universal SQL client.
Code examples
A JPA entity and Spring Data repositoryjava @Entity @Table(name = "tasks") public class Task { @Id @GeneratedValue private Long id; private String title; private boolean done; // getters/setters } public interface TaskRepository extends JpaRepository<Task, Long> { List<Task> findByDoneFalse(); }Common pitfalls
- N+1 queries from lazy associations (use joins or `@EntityGraph`)
- Schema changes applied manually instead of via migrations
- Using `EAGER` fetching everywhere — kills performance
Related tutorials on MasterLabLearn
3
Validation & error handling
Bean Validation, global exception handlers and consistent error responses.
Overview
Every input from the outside world is hostile until validated. Use Bean Validation (`@NotBlank`, `@Size`, `@Email`) on DTOs and let Spring trigger validation with `@Valid`. Centralize error handling in a `@RestControllerAdvice` so every endpoint returns the same error shape.
Distinguish between client errors (`4xx`) and server errors (`5xx`). Never leak stack traces or internal types to clients. Log enough context (request ID, user ID) to debug production issues quickly.
A consistent error contract is one of the highest-leverage things you can do for the teams consuming your API.
Learning objectives
- Annotate DTOs with Bean Validation constraints
- Build a `@RestControllerAdvice` that returns a consistent error shape
- Map domain exceptions to HTTP status codes correctly
- Log enough context to debug a production incident
Practical examples
- RFC 7807 Problem Details response on validation failures
- Domain exceptions mapped to 404, 409 and 422
- Request correlation ID propagated through logs
Recommended tools
- Hibernate Validator
Reference implementation of Bean Validation.
- Spring Boot Actuator
Health, metrics and request logging out of the box.
Code examples
Global exception handlerjava @RestControllerAdvice public class ApiExceptionHandler { @ExceptionHandler(NotFoundException.class) public ResponseEntity<Map<String, Object>> notFound(NotFoundException e) { return ResponseEntity.status(404).body(Map.of("error", e.getMessage())); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<Map<String, Object>> badRequest(MethodArgumentNotValidException e) { return ResponseEntity.badRequest().body(Map.of("errors", e.getBindingResult().getFieldErrors())); } }Common pitfalls
- Trusting client input without validation
- Returning different error shapes from different endpoints
- Leaking stack traces to API consumers
4
Authentication & authorization
Spring Security, JWT, OAuth2 and role-based access control.
Overview
Auth is one of the highest-risk areas in any backend. Learn how Spring Security's filter chain works, when to use sessions vs stateless JWTs, and how OAuth2/OIDC flows let you delegate sign-in to Google, GitHub or your own identity provider.
Implement role-based access at both the controller (with `@PreAuthorize`) and service layers — never trust a client-side role check. Hash passwords with BCrypt or Argon2; never store them in plain text.
When in doubt, lean on the platform. Most teams should use a managed identity provider (Auth0, Cognito, Keycloak, Supabase Auth) rather than rolling their own.
Learning objectives
- Configure Spring Security with a stateless JWT filter chain
- Hash passwords with BCrypt and store them safely
- Protect endpoints with role-based and method-level security
- Integrate an OAuth2 provider (Google / GitHub) for social login
Practical examples
- JWT-based login + refresh token flow
- Admin-only endpoints protected with `@PreAuthorize`
- Sign-in with Google using Spring Security OAuth2
Recommended tools
- Spring Security
Industry-standard auth framework for Spring apps.
- Keycloak
Self-hosted, open-source identity provider.
- jjwt
JWT library for Java.
Code examples
Locking down an endpoint with method-level securityjava @RestController @RequestMapping("/api/admin") public class AdminController { @PreAuthorize("hasRole('ADMIN')") @GetMapping("/stats") public Stats stats() { /* ... */ } }Common pitfalls
- Storing plain-text passwords
- Putting secrets/JWT signing keys in the repo
- Disabling CSRF protection on stateful endpoints
Related tutorials on MasterLabLearn
Stage tools:Spring BootPostgreSQLFlywaySpring Security - 3Advanced
Scaling, Messaging & System Design
Scale beyond a monolith with caching, messaging, microservices and observability.
1
Caching with Redis
Cache-aside, TTLs, invalidation strategies and Redis data structures.
Overview
Caching is the easiest way to make a slow system fast — and the easiest way to introduce nasty bugs. Learn the cache-aside pattern, when to use write-through, and how to choose TTLs based on how stale data is allowed to be.
Redis is the default cache for most Java backends. Beyond simple key-value, learn its data structures (hashes, lists, sets, sorted sets) and use cases like rate limiting, session storage and leaderboards.
Spring's `@Cacheable` makes the basics trivial; the hard part is invalidation. Plan for it from day one.
Learning objectives
- Apply the cache-aside pattern with Spring's caching abstraction
- Choose appropriate TTLs and eviction policies
- Implement rate limiting with Redis
- Avoid stampedes with single-flight or staggered expiry
Practical examples
- Cache product detail responses with a 5-minute TTL
- IP-based rate limiter using `INCR` and `EXPIRE`
- Leaderboard using a Redis sorted set
Recommended tools
- Redis
Fast in-memory cache and data structure server.
- Spring Data Redis
Spring integration with templates and `@Cacheable`.
- RedisInsight
GUI for browsing keys and running queries.
Code examples
Spring @Cacheable for an expensive calljava @Service public class ProductService { @Cacheable(value = "products", key = "#id") public Product getProduct(String id) { return repository.loadFromDb(id); // expensive } @CacheEvict(value = "products", key = "#id") public void update(String id, Product p) { repository.save(p); } }Common pitfalls
- Caching without invalidation — stale data forever
- Caching personalized responses globally (leaks data across users)
- Treating Redis as durable storage (it's primarily a cache)
Related tutorials on MasterLabLearn
2
Messaging with Kafka
Producers, consumers, topics, partitions and the outbox pattern.
Overview
Async messaging decouples services so one slow consumer can't take the whole system down. Kafka is the default for event streams — durable, partitioned, replayable. RabbitMQ is great when you need traditional queues with routing.
Learn producers, consumers, consumer groups and partition keys (they determine ordering). For reliable publish-on-commit, use the transactional outbox pattern: write the event to an `outbox` table in the same DB transaction, then have a relay forward it to Kafka.
Embrace eventual consistency — most real systems are already eventually consistent whether you admit it or not.
Learning objectives
- Produce and consume messages with Spring for Apache Kafka
- Choose partition keys that preserve required ordering
- Implement the transactional outbox pattern
- Handle retries, dead-letter topics and idempotent consumers
Practical examples
- Emit `OrderPlaced` events that fan out to email + analytics services
- Outbox table relay using a scheduled job or Debezium
- Dead-letter topic for poison messages
Recommended tools
- Apache Kafka
Industry-standard event streaming platform.
- Spring for Apache Kafka
Spring integration with `KafkaTemplate` and `@KafkaListener`.
- Conduktor / Offset Explorer
GUI for inspecting topics and offsets.
Code examples
Producing and consuming a Kafka eventjava @Service public class OrderEvents { private final KafkaTemplate<String, String> template; public OrderEvents(KafkaTemplate<String, String> template) { this.template = template; } public void emitPlaced(String orderId) { template.send("orders.placed", orderId, orderId); } } @KafkaListener(topics = "orders.placed", groupId = "email") public void onPlaced(String orderId) { /* send email */ }Common pitfalls
- Publishing the event before the DB commit (loses messages on rollback)
- Non-idempotent consumers (duplicate processing on retry)
- Choosing partition keys that create hot partitions
Related tutorials on MasterLabLearn
3
Microservices architecture
Service boundaries, gateways, discovery, resilience and observability.
Overview
Microservices solve organizational scaling problems more than technical ones. Draw service boundaries around business capabilities (Domain-Driven Design), centralize cross-cutting concerns (auth, routing, rate limiting) in an API gateway, and use service discovery so services can find each other.
The moment you split a monolith, network failures become a first-class concern. Add retries with backoff, timeouts and circuit breakers (Resilience4j). Standardize logging, metrics and distributed tracing so you can debug across service boundaries.
Don't start with microservices. Earn them — extract a service only when you have a clear pain point a separate deployment solves.
Learning objectives
- Identify good service boundaries using DDD bounded contexts
- Configure an API gateway for routing, auth and rate limiting
- Add circuit breakers, timeouts and retries with Resilience4j
- Trace a request across multiple services with OpenTelemetry
Practical examples
- E-commerce backend split into `orders`, `payments`, `inventory`
- Spring Cloud Gateway in front of internal services
- Circuit breaker that fails fast when payments is down
Recommended tools
- Spring Cloud Gateway
Reactive API gateway built on Spring.
- Resilience4j
Lightweight fault tolerance library (retries, circuit breakers).
- OpenTelemetry
Vendor-neutral standard for traces, metrics and logs.
Code examples
Resilience4j circuit breaker around an HTTP calljava @CircuitBreaker(name = "payments", fallbackMethod = "fallback") public PaymentResult charge(String orderId, double amount) { return paymentsClient.charge(orderId, amount); } private PaymentResult fallback(String orderId, double amount, Throwable t) { return PaymentResult.deferred(orderId); }Common pitfalls
- Starting microservices on day one (premature distribution)
- Shared databases across services (recreates a distributed monolith)
- No distributed tracing — debugging becomes archaeology
Related tutorials on MasterLabLearn
4
System design & testing
Capacity planning, CAP tradeoffs, and the testing pyramid for backends.
Overview
System design is the art of making tradeoffs. Learn the building blocks — load balancers, caches, queues, replication, sharding — and the constraints (CAP, latency, cost). Practice back-of-the-envelope estimation: knowing your QPS, payload size and storage growth tells you when to scale before you have to.
For testing, follow the pyramid: many fast unit tests, fewer integration tests, a small set of end-to-end tests. Use Testcontainers to spin up a real PostgreSQL/Kafka in CI so integration tests catch bugs mocks never will.
These are the skills that separate senior engineers from mid-level ones.
Learning objectives
- Estimate QPS, storage and bandwidth for a proposed system
- Explain CAP and PACELC tradeoffs with concrete examples
- Write JUnit 5 + Mockito unit tests and Spring slice tests
- Spin up real dependencies in CI with Testcontainers
Practical examples
- Design a URL shortener: estimate writes/reads, choose storage
- Design a notification fan-out service
- Integration test using `@SpringBootTest` + Testcontainers PostgreSQL
Recommended tools
- JUnit 5
Standard test framework for the JVM.
- Mockito
Mocking library for unit tests.
- Testcontainers
Real Docker-backed dependencies in your tests.
Code examples
Spring MVC slice test for a controllerjava @WebMvcTest(TaskController.class) class TaskControllerTest { @Autowired MockMvc mvc; @MockBean TaskService service; @Test void listsTasks() throws Exception { when(service.findAll()).thenReturn(List.of(new TaskDto(1L, "Write tests", false))); mvc.perform(get("/api/tasks")) .andExpect(status().isOk()) .andExpect(jsonPath("$[0].title").value("Write tests")); } }Common pitfalls
- Designing for hypothetical scale instead of real numbers
- Mocking everything — integration tests with real deps catch more bugs
- Skipping load tests until production
Related tutorials on MasterLabLearn
Stage tools:RedisKafkaSpring CloudPrometheusTestcontainers
A practical path through Linux, containers, orchestration, CI/CD and the cloud — the toolkit every modern engineer needs.
Open full page →- 1Beginner
Linux, Git & Command Line
Get comfortable on the command line and with the version control workflows every team uses.
1
Linux & command line basics
Filesystem, permissions, processes, networking and shell scripting.
Overview
Almost every server you'll touch runs Linux. Learn the filesystem hierarchy, file permissions and ownership, how to inspect running processes (`ps`, `top`, `htop`), and how to read system logs with `journalctl` or `tail -f`.
The faster you move in a terminal, the faster you diagnose production issues. Get comfortable with `grep`, `sed`, `awk`, `find`, `xargs` and shell pipelines — they replace entire scripts when used well.
This is non-negotiable for any DevOps, SRE or backend engineer role.
Learning objectives
- Navigate the filesystem and manage files/permissions
- Inspect processes, ports and system resource usage
- Read logs and trace recent system activity
- Write small Bash scripts to automate repetitive tasks
Practical examples
- Bash script that rotates and gzip-compresses log files
- One-liner that finds the largest files in a directory
- Crontab entry that runs a backup every night
Recommended tools
- Ubuntu / Debian
Most common Linux distros for servers.
- tmux
Persistent terminal sessions over SSH.
- ShellCheck
Linter that catches common Bash mistakes.
Code examples
A small, well-behaved Bash scriptbash #!/usr/bin/env bash set -euo pipefail DIR=${1:-/var/log/app} echo "Compressing logs older than 7 days in $DIR" find "$DIR" -type f -name "*.log" -mtime +7 -exec gzip {} \;Common pitfalls
- Running scripts without `set -euo pipefail` (errors silently ignored)
- Editing production files directly without version control
- Using `chmod 777` to 'fix' permission issues
2
Git & GitHub workflows
Branching, pull requests, code review, rebasing and conflict resolution.
Overview
DevOps lives inside Git. Beyond `add`/`commit`/`push`, learn trunk-based development, how to write atomic commits with clear messages, and how to resolve merge conflicts without panicking.
Understand the difference between `merge` and `rebase` — and when to use each. Learn `git reflog` so you can recover from almost any mistake. Get comfortable with pull requests, code review etiquette and protected branches.
A solid Git workflow is what enables the rest of DevOps: every deployment, every rollback, every audit trail starts here.
Learning objectives
- Use feature branches and open well-described pull requests
- Rebase a branch on top of `main` and resolve conflicts
- Recover lost commits using `git reflog`
- Configure protected branches and required reviews on GitHub
Practical examples
- Trunk-based workflow with short-lived feature branches
- Interactive rebase to clean up a messy commit history
- GitHub branch protection requiring CI green + 1 approval
Recommended tools
- Git
The version control system; install locally.
- GitHub
Most popular host for code, PRs and Actions.
- GitLens (VS Code)
Inline blame, history and rich Git UI.
Code examples
Rebase a feature branch and force-push safelybash git fetch origin git checkout feature/login git rebase origin/main # resolve conflicts, then: git push --force-with-leaseCommon pitfalls
- Force-pushing to shared branches without `--force-with-lease`
- Giant 'wip' commits instead of atomic, reviewable ones
- Committing secrets — once pushed, treat them as leaked
Stage tools:UbuntuGitGitHubBash - 2Intermediate
Docker & CI/CD
Package applications consistently and automate their path from commit to production.
1
Docker fundamentals
Images, containers, volumes, networks and multi-stage Dockerfiles.
Overview
Docker solves the 'works on my machine' problem by packaging an app with its entire runtime into an image. Learn the difference between images and containers, how layers and the build cache work, and how to write small, secure Dockerfiles.
Use multi-stage builds to keep production images tiny — compile in a build stage, copy only the artifact into a minimal runtime stage (distroless, Alpine). Always run as a non-root user.
Docker is the foundation everything else builds on: CI/CD, Kubernetes, local dev environments.
Learning objectives
- Write a multi-stage Dockerfile that produces a small image
- Run containers with port, volume and env-var configuration
- Use `.dockerignore` to keep build context small
- Tag and push images to a container registry
Practical examples
- Dockerize a Spring Boot app into a 100MB image
- Local dev environment with hot reload via bind mounts
- Build a Node.js image as a non-root user
Recommended tools
- Docker Desktop
Run Docker locally on Mac/Windows/Linux.
- Hadolint
Linter for Dockerfiles.
- Dive
Inspect image layers and find waste.
Code examples
Multi-stage Dockerfile for a Spring Boot appdockerfile FROM eclipse-temurin:21-jdk AS build WORKDIR /app COPY . . RUN ./mvnw -q -DskipTests package FROM eclipse-temurin:21-jre WORKDIR /app COPY --from=build /app/target/*.jar app.jar USER 1000 EXPOSE 8080 ENTRYPOINT ["java","-jar","/app/app.jar"]Common pitfalls
- Running containers as root by default
- Putting secrets in the image via `ENV`
- Skipping `.dockerignore` (bloats build context and image)
Related tutorials on MasterLabLearn
2
Docker Compose
Multi-container local environments with dependent services.
Overview
Docker Compose lets you declare a multi-service local environment in a single `docker-compose.yml` file — your app, a database, Redis, Kafka, whatever you need. One command brings the whole stack up.
Use it for local development, integration tests and small single-host deployments. For production at scale, you'll graduate to Kubernetes, but Compose remains invaluable for day-to-day work.
Keep secrets out of the file — use a `.env` file (gitignored) and environment substitution.
Learning objectives
- Define a multi-service stack in `docker-compose.yml`
- Use named volumes for persistent data
- Wire services together with named networks and DNS
- Override config per environment with `docker-compose.override.yml`
Practical examples
- Spring Boot + PostgreSQL + Redis stack for local dev
- Kafka + Zookeeper for local event-driven development
- Full LGTM observability stack (Loki, Grafana, Tempo, Mimir)
Recommended tools
- Docker Compose v2
The modern `docker compose` plugin.
- Portainer
Web UI for managing local Docker stacks.
Code examples
App + Postgres + Redis with Docker Composeyaml services: app: build: . ports: ["8080:8080"] environment: DB_URL: jdbc:postgresql://db:5432/app depends_on: [db, cache] db: image: postgres:16 environment: POSTGRES_PASSWORD: secret volumes: ["pgdata:/var/lib/postgresql/data"] cache: image: redis:7 volumes: pgdata:Common pitfalls
- Committing `.env` files with real credentials
- Using `latest` image tags (non-reproducible builds)
- Forgetting volumes — your local data disappears on `docker compose down -v`
Related tutorials on MasterLabLearn
3
CI/CD with GitHub Actions
Workflows for build, test, security scan and deploy.
Overview
Continuous integration runs your tests on every push; continuous delivery deploys passing builds automatically. GitHub Actions is the most common choice today — workflows live in `.github/workflows/*.yml` and run on GitHub-hosted runners.
A mature pipeline lints, type-checks, tests, builds, scans for vulnerabilities (Trivy, Snyk) and pushes a tagged container image to a registry. Then it deploys to staging on merge and to production on tag.
Fast, reliable pipelines are the backbone of a high-velocity engineering team.
Learning objectives
- Write a GitHub Actions workflow with multiple jobs
- Cache dependencies to speed up builds
- Build and push a Docker image to GHCR
- Add manual approval gates for production deploys
Practical examples
- Workflow that runs Vitest + Playwright in parallel
- Build-push-deploy pipeline for a Spring Boot service
- Nightly security scan with Trivy on the latest image
Recommended tools
- GitHub Actions
Native CI/CD for GitHub repos.
- Trivy
Open-source vulnerability scanner for images and IaC.
- GHCR
GitHub's free container registry.
Code examples
Build, test and push a Docker imageyaml name: ci on: push: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/build-push-action@v6 with: push: true tags: ghcr.io/${{ github.repository }}:${{ github.sha }}Common pitfalls
- Storing secrets in workflow YAML instead of GitHub Secrets
- Re-running full builds when nothing changed (no caching)
- Auto-deploying to production without manual approval gates
Related tutorials on MasterLabLearn
Stage tools:DockerDocker ComposeGitHub ActionsTrivy - 3Advanced
Kubernetes, Cloud & Observability
Run resilient workloads on Kubernetes and the major cloud providers with full observability and IaC.
1
Kubernetes basics
Pods, Deployments, Services, Ingress and ConfigMaps.
Overview
Kubernetes orchestrates containers at scale: it schedules them, restarts them when they crash, and rolls out new versions without downtime. The core objects are Pods (one or more containers), Deployments (declarative rollout of pods), Services (stable network endpoints) and Ingress (HTTP routing).
Start with a local cluster (kind, minikube, k3d) and deploy a simple app behind a Service and Ingress. Learn `kubectl` deeply — you'll live in it.
Kubernetes is overkill for small projects but the de-facto standard the moment you have multiple services and real uptime requirements.
Learning objectives
- Write Deployment, Service and Ingress manifests
- Use ConfigMaps and Secrets for configuration
- Roll out and roll back versions safely
- Diagnose pod issues with `kubectl describe` and `logs`
Practical examples
- Deploy a Spring Boot app behind an NGINX Ingress
- Horizontal Pod Autoscaler that scales on CPU
- Blue-green deploy using two Deployments and a Service swap
Recommended tools
- kubectl
The Kubernetes CLI — your daily driver.
- kind / minikube
Run a local Kubernetes cluster for learning.
- k9s
Terminal UI for browsing clusters quickly.
Code examples
Deployment + Service for a web appyaml apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 3 selector: { matchLabels: { app: web } } template: metadata: { labels: { app: web } } spec: containers: - name: web image: ghcr.io/acme/web:1.2.0 ports: [{ containerPort: 8080 }] --- apiVersion: v1 kind: Service metadata: { name: web } spec: selector: { app: web } ports: [{ port: 80, targetPort: 8080 }]Common pitfalls
- No resource requests/limits — one pod eats the whole node
- Storing secrets in plain ConfigMaps instead of Secrets (or Vault)
- Skipping liveness/readiness probes — bad rollouts go unnoticed
Related tutorials on MasterLabLearn
2
Helm
Package, version and parameterize Kubernetes manifests.
Overview
Helm is the package manager for Kubernetes. A Helm chart bundles your manifests with a `values.yaml` so the same chart deploys to dev, staging and prod with different settings (replicas, image tag, env vars).
Use community charts for off-the-shelf software (Postgres, Redis, ingress controllers) and write your own for your services. Pin chart versions and store overrides per environment in Git (GitOps).
This is what makes Kubernetes manageable at scale.
Learning objectives
- Create a Helm chart for your service
- Override values per environment
- Install community charts (e.g. ingress-nginx, cert-manager)
- Adopt a GitOps workflow with values files in Git
Practical examples
- Helm chart for a Spring Boot app with `values-prod.yaml`
- Install ingress-nginx + cert-manager for TLS
- ArgoCD syncing a chart from a Git repo
Recommended tools
- Helm 3
The Kubernetes package manager.
- ArgoCD
Declarative GitOps continuous delivery for K8s.
- kubeval / kubeconform
Validate manifests against the K8s schema.
Code examples
values.yaml for a Helm chartyaml image: repository: ghcr.io/acme/web tag: "1.2.0" replicaCount: 3 resources: requests: { cpu: "100m", memory: "256Mi" } limits: { cpu: "500m", memory: "512Mi" } ingress: enabled: true host: app.example.comCommon pitfalls
- Using `latest` image tags inside Helm values
- Editing released manifests directly instead of upgrading the chart
- Storing prod values in the same file as dev
Related tutorials on MasterLabLearn
3
Cloud fundamentals (AWS & Azure)
Compute, storage, IAM and managed databases on the big clouds.
Overview
Pick one cloud (AWS, Azure or GCP) and go deep before going broad. Learn the core building blocks: compute (EC2, ECS, Lambda / App Service, Functions), storage (S3, EBS / Blob Storage), networking (VPC, security groups), IAM (least-privilege roles) and managed databases (RDS, DynamoDB / Azure SQL, Cosmos DB).
Most teams misuse IAM — getting it right is the single biggest security win. Use roles, not long-lived access keys, and grant the minimum permissions a workload needs.
Managed services beat self-hosted nearly every time for a small team — let the cloud handle backups, patching and HA.
Learning objectives
- Deploy a containerized service to ECS Fargate or Azure Container Apps
- Configure VPC, subnets and security groups correctly
- Apply least-privilege IAM policies
- Use a managed database (RDS / Azure SQL) with automated backups
Practical examples
- Spring Boot service on ECS Fargate behind an ALB
- S3 + CloudFront for static site hosting
- Azure App Service with managed identity to access Key Vault
Recommended tools
- AWS CLI / Azure CLI
Script and automate cloud operations.
- AWS Console / Azure Portal
Discover services and inspect state.
- LocalStack
Run AWS-compatible services locally for tests.
Code examples
Deploy to ECS Fargate with the AWS CLIbash aws ecs update-service \ --cluster prod \ --service web \ --force-new-deployment \ --region us-east-1Common pitfalls
- Long-lived IAM access keys committed to repos
- Public S3 buckets / storage accounts (data leaks)
- Manually clicking in the console instead of using IaC
Related tutorials on MasterLabLearn
4
Infrastructure as Code (Terraform)
Modules, remote state, workspaces and reproducible environments.
Overview
Click-ops doesn't scale. Define every cloud resource in Terraform (or Pulumi, OpenTofu) so environments are reproducible, reviewable in pull requests, and recoverable from disaster.
Learn modules (reusable building blocks), remote state (stored in S3/Azure Blob with locking), and workspaces or directory layouts for multiple environments. Run `terraform plan` in CI on every PR and `apply` only after review.
This is the difference between an infrastructure that survives staff turnover and one that doesn't.
Learning objectives
- Write modular Terraform for a real service (network, compute, DB)
- Configure remote state with locking
- Split environments cleanly (dev, staging, prod)
- Wire `plan` into CI on every pull request
Practical examples
- Terraform module for an ECS service + ALB + security groups
- Reusable VPC module imported by multiple environments
- GitHub Actions workflow running `terraform plan` on PRs
Recommended tools
- Terraform
Most widely used IaC tool.
- OpenTofu
Open-source fork of Terraform.
- Atlantis / Terraform Cloud
Run plan/apply via PRs with state management.
Code examples
A minimal Terraform module callhcl module "web" { source = "./modules/ecs-service" service_name = "web" image = "ghcr.io/acme/web:1.2.0" cpu = 512 memory = 1024 vpc_id = module.network.vpc_id subnet_ids = module.network.private_subnet_ids }Common pitfalls
- Local state files — no collaboration, no locking, easy to lose
- Hard-coded secrets in `.tf` files (use a secrets manager)
- Running `apply` without reviewing `plan` output
5
Monitoring, logging & security
Prometheus, Grafana, Loki, alerting and baseline cloud security.
Overview
Production isn't done at deploy — it's done when you can sleep through the night. Collect metrics with Prometheus, visualize them in Grafana, centralize logs with Loki or the ELK stack, and define SLOs that drive meaningful alerts (page on symptoms, not causes).
On the security side: enforce least-privilege IAM, rotate secrets via a vault (HashiCorp Vault, AWS Secrets Manager), scan images and IaC for vulnerabilities (Trivy, Checkov), and enable audit logging on every account.
Run blameless post-mortems so every incident makes the system stronger.
Learning objectives
- Instrument a service with Prometheus metrics
- Build a Grafana dashboard tracking the RED metrics
- Define SLOs and alerts that page only on user-visible issues
- Run vulnerability scans on images and IaC in CI
Practical examples
- RED dashboard (Rate, Errors, Duration) for an API service
- Alert when error rate exceeds 1% for 5 minutes
- Trivy scan job that fails CI on critical CVEs
Recommended tools
- Prometheus
Standard open-source metrics database.
- Grafana
Best-in-class dashboards and alerting UI.
- Loki
Log aggregation that integrates natively with Grafana.
- Trivy
Vulnerability scanning for images and IaC.
Code examples
A Prometheus alert ruleyaml groups: - name: api rules: - alert: HighErrorRate expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) > 0.01 for: 5m labels: { severity: page } annotations: summary: "API error rate > 1% for 5m"Common pitfalls
- Alert fatigue — paging on causes instead of symptoms
- No log retention policy (either too short or unbounded cost)
- Skipping security scans 'until later' — later never comes
Stage tools:KubernetesHelmTerraformPrometheusGrafana
