Web development is one of the most accessible tech careers in 2025. Businesses need websites, web apps, and APIs — and that demand keeps growing. This roadmap shows you exactly what to learn, in what order, and what to skip so you don't waste months going down the wrong path.
At a glance
| Phase | Topics | Time estimate |
|---|---|---|
| 0 | Computer basics, terminal, how the web works | 1–2 weeks |
| 1 | HTML — structure and semantics | 2–3 weeks |
| 2 | CSS — styling and layout | 4–6 weeks |
| 3 | JavaScript — the programming language of the web | 8–12 weeks |
| 4 | Git and version control | 1–2 weeks |
| 5 | A front-end framework (React, Vue, or Svelte) | 6–10 weeks |
| 6 | Back-end basics — Node.js or Python | 6–8 weeks |
| 7 | Databases — SQL and/or NoSQL | 3–4 weeks |
| 8 | APIs — building and consuming | 2–3 weeks |
| 9 | Deployment and DevOps basics | 2–3 weeks |
| 10 | Portfolio projects and job search | 4–8 weeks |
| Total to first junior job | ~12–18 months |
Phase 0 — Foundations (Weeks 1–2)
Before writing a single line of code, get comfortable with the tools every developer uses.
How the web works
User types URL → DNS lookup → TCP connection → HTTP request → Server response → Browser renders
Key concepts to understand:
- Browser renders HTML, CSS, JavaScript
- HTTP/HTTPS is the communication protocol
- Client = browser; Server = the computer serving your files/data
- Domain points to an IP address via DNS
- Static vs dynamic websites (static = same for everyone; dynamic = personalised content)
Terminal basics
You don't need to be a Linux expert, but learn these:
pwd # where am I?
ls # list files
cd folder/ # change directory
mkdir mysite # create folder
touch index.html # create file
cp a.txt b.txt # copy file
mv a.txt b.txt # move / rename
rm file.txt # delete file
Text editor
Use VS Code — free, fast, and has the best extension ecosystem. Install these extensions:
- Prettier (auto-formatting)
- ESLint (JavaScript linting)
- Live Server (instant browser reload)
- GitLens (better Git integration)
Phase 1 — HTML (Weeks 3–5)
HTML is the skeleton of every webpage. It defines structure — not appearance.
Core concepts
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My First Page</title>
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<h1>Welcome</h1>
<p>This is a paragraph with <strong>bold</strong> and <em>italic</em> text.</p>
<ul>
<li>Item one</li>
<li>Item two</li>
</ul>
<img src="photo.jpg" alt="A descriptive alt text" />
<a href="https://example.com">External link</a>
</main>
<footer>
<p>© 2025 My Site</p>
</footer>
</body>
</html>
Semantic HTML elements
| Element | Purpose |
|---|---|
<header> |
Top section of page or article |
<nav> |
Navigation links |
<main> |
Primary content (one per page) |
<article> |
Self-contained content (blog post, product card) |
<section> |
Thematic grouping within a page |
<aside> |
Supplementary content (sidebar) |
<footer> |
Bottom section |
<figure> / <figcaption> |
Image with caption |
<time> |
Dates and times |
Semantic HTML matters for:
- SEO — search engines understand your content structure
- Accessibility — screen readers navigate by landmarks
- Maintainability — code is self-documenting
HTML forms
<form action="/submit" method="POST">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required />
<label for="message">Message:</label>
<textarea id="message" name="message" rows="4"></textarea>
<button type="submit">Send</button>
</form>
Phase 2 — CSS (Weeks 6–11)
CSS controls appearance — colors, fonts, spacing, layout.
The box model
Every element is a box:
┌──────────────────────────┐
│ margin │
│ ┌────────────────────┐ │
│ │ border │ │
│ │ ┌──────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌────────┐ │ │ │
│ │ │ │content │ │ │ │
│ │ │ └────────┘ │ │ │
│ │ └──────────────┘ │ │
│ └────────────────────┘ │
└──────────────────────────┘
/* Always set this at the top */
*, *::before, *::after {
box-sizing: border-box; /* padding/border included in width */
}
.card {
width: 300px;
padding: 20px;
border: 1px solid #e5e7eb;
border-radius: 8px;
margin: 16px;
}
Flexbox (one-dimensional layout)
.container {
display: flex;
justify-content: space-between; /* horizontal */
align-items: center; /* vertical */
gap: 16px;
flex-wrap: wrap;
}
.item {
flex: 1; /* grow equally */
min-width: 200px; /* don't shrink below 200px */
}
| Property | Values | Effect |
|---|---|---|
justify-content |
flex-start, center, space-between, space-around, space-evenly |
Main axis alignment |
align-items |
stretch, center, flex-start, flex-end, baseline |
Cross axis alignment |
flex-direction |
row, column, row-reverse, column-reverse |
Main axis direction |
flex-wrap |
nowrap, wrap, wrap-reverse |
Allow wrapping |
gap |
length | Space between items |
CSS Grid (two-dimensional layout)
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3 equal columns */
gap: 24px;
}
/* Responsive grid — columns collapse on small screens */
.responsive-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
}
Responsive design
/* Mobile first */
.container {
padding: 16px;
max-width: 100%;
}
/* Tablet */
@media (min-width: 768px) {
.container {
padding: 24px;
max-width: 720px;
margin: 0 auto;
}
}
/* Desktop */
@media (min-width: 1024px) {
.container {
max-width: 960px;
}
}
Standard breakpoints:
480px— large phones768px— tablets1024px— laptops1280px— desktops
CSS custom properties (variables)
:root {
--color-primary: #3b82f6;
--color-text: #111827;
--spacing-sm: 8px;
--spacing-md: 16px;
--border-radius: 6px;
--font-body: 'Inter', sans-serif;
}
.button {
background: var(--color-primary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
}
Phase 3 — JavaScript (Weeks 12–23)
JavaScript makes pages interactive — form validation, dynamic content, API calls.
JavaScript basics
// Variables
const name = 'Alice'; // immutable binding
let count = 0; // mutable
// var is outdated — avoid it
// Functions
function greet(name) {
return `Hello, ${name}!`;
}
// Arrow function
const double = (n) => n * 2;
// Conditionals
if (count > 10) {
console.log('high');
} else if (count > 5) {
console.log('medium');
} else {
console.log('low');
}
// Arrays
const fruits = ['apple', 'banana', 'cherry'];
fruits.push('date');
const upper = fruits.map(f => f.toUpperCase());
const long = fruits.filter(f => f.length > 5);
// Objects
const user = { name: 'Alice', age: 30 };
const { name: userName, age } = user; // destructuring
DOM manipulation
// Select elements
const btn = document.querySelector('#my-button');
const items = document.querySelectorAll('.item');
// Modify content
btn.textContent = 'Click me';
btn.classList.add('active');
btn.classList.toggle('hidden');
// Events
btn.addEventListener('click', (event) => {
event.preventDefault();
console.log('Button clicked!');
});
// Create elements
const div = document.createElement('div');
div.textContent = 'New content';
document.body.appendChild(div);
Async JavaScript
// Fetch data from an API
async function fetchUsers() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
const data = await response.json();
return data;
} catch (error) {
console.error('Fetch failed:', error);
}
}
// Use it
fetchUsers().then(users => {
users.forEach(user => console.log(user.name));
});
What to learn in JavaScript
| Topic | Why it matters |
|---|---|
| Variables, data types, operators | Foundation |
| Functions, arrow functions, scope | Code organisation |
| Arrays and array methods (map/filter/reduce) | Data transformation |
| Objects and destructuring | Working with data |
| DOM manipulation | Making pages interactive |
| Events and event delegation | User interaction |
fetch and async/await |
Calling APIs |
| Error handling (try/catch) | Robust code |
| ES6+ features (spread, optional chaining, nullish coalescing) | Modern syntax |
| Modules (import/export) | Code splitting |
Phase 4 — Git (Weeks 24–25)
Version control is non-negotiable. Every professional developer uses Git.
Essential commands
git init # start new repo
git clone <url> # copy existing repo
git status # see what changed
git add . # stage all changes
git commit -m "Add feature" # save snapshot
git push origin main # upload to GitHub
git pull # download latest changes
# Branching
git checkout -b feature/login # create + switch branch
git merge feature/login # merge into current branch
git branch -d feature/login # delete branch after merge
Git workflow
main branch (always deployable)
└── feature/user-auth ← you work here
└── fix/navbar-bug ← colleague works here
└── feature/dark-mode ← another feature
Create a GitHub account and push every project you build. Employers look at your GitHub profile.
Phase 5 — Front-end framework (Weeks 26–35)
Once you're comfortable with JavaScript, learn one front-end framework.
Which framework?
| Framework | Learning curve | Job demand | Best for |
|---|---|---|---|
| React | Moderate | Very high (60-70% of jobs) | General web apps, startups, large teams |
| Vue 3 | Low | Moderate | Smaller teams, beginners, existing PHP/Laravel projects |
| Svelte | Low | Low but growing | Performance-focused, simpler mental model |
| Angular | High | Moderate | Large enterprise projects, TypeScript-first teams |
Recommendation for beginners: React. It has the most jobs, tutorials, and community support.
React fundamentals
import { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
});
}, []); // empty array = run once on mount
if (loading) return <p>Loading...</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
React concepts to learn
| Concept | What it does |
|---|---|
| Components | Reusable UI pieces |
| Props | Pass data into components |
State (useState) |
Local mutable data |
Effect (useEffect) |
Side effects (data fetching, subscriptions) |
Context (useContext) |
Share data without prop drilling |
Ref (useRef) |
Access DOM elements directly |
Memo (useMemo, useCallback) |
Optimise re-renders |
| React Router | Multi-page navigation in a SPA |
| Data fetching (TanStack Query) | Server state management |
Phase 6 — Back-end basics (Weeks 36–43)
The back end handles data storage, business logic, authentication, and APIs.
Choose a language
| Language | Framework | Best for |
|---|---|---|
| JavaScript/TypeScript | Node.js + Express / Fastify | Full-stack JS teams, shared code with front end |
| Python | FastAPI / Django | Data science, ML integration, clean syntax |
| Go | standard library / Gin | High-performance APIs, microservices |
| PHP | Laravel | Traditional web apps, WordPress ecosystem |
Recommendation for beginners: Node.js — same language as your front end, huge ecosystem.
Node.js + Express basics
import express from 'express';
const app = express();
app.use(express.json());
// In-memory "database" for learning
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
// GET all users
app.get('/api/users', (req, res) => {
res.json(users);
});
// GET single user
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === Number(req.params.id));
if (!user) return res.status(404).json({ error: 'User not found' });
res.json(user);
});
// POST create user
app.post('/api/users', (req, res) => {
const user = { id: Date.now(), ...req.body };
users.push(user);
res.status(201).json(user);
});
// DELETE user
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === Number(req.params.id));
if (index === -1) return res.status(404).json({ error: 'Not found' });
users.splice(index, 1);
res.status(204).send();
});
app.listen(3000, () => console.log('Server running on port 3000'));
Phase 7 — Databases (Weeks 44–47)
Almost every web app needs to store data persistently.
SQL vs NoSQL
| Aspect | SQL (relational) | NoSQL (document) |
|---|---|---|
| Structure | Tables with fixed schema | Flexible JSON documents |
| Relationships | JOINs, foreign keys | Embedded documents or references |
| Query language | SQL | Database-specific API |
| Best for | Structured data, complex queries, transactions | Flexible schema, horizontal scale |
| Examples | PostgreSQL, MySQL, SQLite | MongoDB, DynamoDB, Firestore |
Recommendation: Learn SQL first. SQL knowledge transfers across databases, it's tested in interviews, and it handles 80% of use cases.
SQL essentials
-- Create table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Insert
INSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice');
-- Select
SELECT id, name, email FROM users WHERE name LIKE 'A%' ORDER BY name;
-- Update
UPDATE users SET name = 'Alice Smith' WHERE id = 1;
-- Delete
DELETE FROM users WHERE id = 1;
-- JOIN
SELECT orders.id, users.name, orders.total
FROM orders
JOIN users ON orders.user_id = users.id
WHERE orders.total > 100;
ORM (Object-Relational Mapper)
Don't write raw SQL for every query. Use an ORM:
| Language | Popular ORMs |
|---|---|
| JavaScript/TypeScript | Prisma, Drizzle, Sequelize |
| Python | SQLAlchemy, Django ORM |
| Java | Hibernate, Spring Data JPA |
| PHP | Eloquent (Laravel) |
// Prisma example
const user = await prisma.user.create({
data: { email: 'alice@example.com', name: 'Alice' },
});
const users = await prisma.user.findMany({
where: { name: { startsWith: 'A' } },
orderBy: { name: 'asc' },
});
Phase 8 — APIs (Weeks 48–50)
REST API conventions
| Method | Path | Action |
|---|---|---|
| GET | /api/users |
List all users |
| GET | /api/users/42 |
Get user 42 |
| POST | /api/users |
Create user |
| PUT | /api/users/42 |
Replace user 42 |
| PATCH | /api/users/42 |
Update fields on user 42 |
| DELETE | /api/users/42 |
Delete user 42 |
Authentication basics
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
// Register
app.post('/api/auth/register', async (req, res) => {
const { email, password, name } = req.body;
const hashedPassword = await bcrypt.hash(password, 12);
const user = await prisma.user.create({
data: { email, name, password: hashedPassword },
});
res.status(201).json({ id: user.id, email: user.email });
});
// Login
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await prisma.user.findUnique({ where: { email } });
if (!user || !await bcrypt.compare(password, user.password)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, {
expiresIn: '7d',
});
res.json({ token });
});
// Protected route middleware
function authenticate(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'Unauthorized' });
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
res.status(401).json({ error: 'Invalid token' });
}
}
Phase 9 — Deployment (Weeks 51–53)
Your app needs to run somewhere accessible to the internet.
Deployment options
| Platform | Type | Free tier | Best for |
|---|---|---|---|
| Vercel | Serverless/static | Yes | Next.js, static sites, front-end |
| Netlify | Static/serverless | Yes | Static sites, JAMstack |
| Railway | Full server | Yes ($5 credit) | Node.js, Postgres, Redis |
| Render | Full server | Yes (spins down) | Full-stack apps, databases |
| Fly.io | Containers | Yes | Docker containers, global edge |
| AWS / GCP / Azure | Cloud | Limited | Production, enterprise |
Deploying a React + Node.js app (example: Vercel + Railway)
# Front end (React/Next.js) → Vercel
npx vercel # follow prompts
# Back end (Node.js API) → Railway
# Push to GitHub → connect Railway to your repo → deploy
Environment variables
# .env (never commit this!)
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
JWT_SECRET=super-secret-key-at-least-32-chars
PORT=3000
// Load in Node.js
import 'dotenv/config';
const db = process.env.DATABASE_URL;
Basics of HTTPS and domains
- Every production site needs HTTPS — Vercel/Netlify/Railway handle this automatically
- Buy a domain from Namecheap, Cloudflare, or Google Domains
- Point domain's DNS A record to your server IP
Full technology map
Web Developer 2025
│
├── HTML ──────── structure, semantics, accessibility, forms
│
├── CSS ───────── box model, flexbox, grid, animations,
│ responsive design, custom properties
│
├── JavaScript ── DOM, events, fetch, async/await, ES6+
│
├── TypeScript ── optional but highly recommended
│
├── Git ───────── version control, GitHub, branching workflow
│
├── Front end ─── React (or Vue / Svelte)
│ ├── Routing (React Router / Next.js)
│ └── State (TanStack Query, Zustand, Context)
│
├── Back end ──── Node.js + Express (or Python / Go)
│ ├── REST APIs
│ ├── Authentication (JWT, sessions)
│ └── Validation (Zod, Joi)
│
├── Database ──── PostgreSQL or MySQL
│ ├── ORM (Prisma, SQLAlchemy)
│ └── NoSQL optional (MongoDB, Redis)
│
├── Deployment ── Vercel / Railway / Render
│ ├── CI/CD (GitHub Actions)
│ └── Environment variables
│
└── Tools ─────── VS Code, Chrome DevTools, Postman, Docker basics
18-month timeline
| Month | Focus | Milestone |
|---|---|---|
| 1 | Terminal, VS Code, HTML | Build a static personal page |
| 2 | CSS basics, flexbox | Build a responsive landing page |
| 3 | CSS grid, animations, media queries | Portfolio site with responsive layout |
| 4 | JavaScript fundamentals | Interactive to-do list |
| 5–6 | JS async, DOM, APIs | Weather app using public API |
| 7 | Git + GitHub | All projects on GitHub |
| 8–9 | React basics | Blog front end with React |
| 10 | React advanced (hooks, routing) | Multi-page React SPA |
| 11 | Node.js + Express | REST API with in-memory data |
| 12 | PostgreSQL + Prisma | API backed by real database |
| 13 | Auth (JWT) | Full authentication system |
| 14 | Full-stack project | Complete app: front end + back end + DB |
| 15 | Deployment | App live on the internet |
| 16 | Second full-stack project | More complex app for portfolio |
| 17 | TypeScript basics | Add TypeScript to both projects |
| 18 | Job search | Resume, applications, interviews |
Portfolio project ideas
| Project | Tech stack | What it shows |
|---|---|---|
| Personal portfolio site | HTML, CSS, JavaScript | HTML/CSS skills, responsiveness |
| Recipe finder | React + public API | React, API integration |
| Task manager | React + Node.js + SQLite | Full-stack CRUD |
| Expense tracker | React + Node.js + PostgreSQL | Auth, database, charts |
| URL shortener | Node.js + Redis | Back-end logic, caching |
| Real-time chat | Node.js + Socket.IO + React | WebSockets |
Web developer roles and salaries (2025)
| Role | Focus | Junior salary (US) | Mid salary (US) |
|---|---|---|---|
| Front-end developer | UI, React/Vue, CSS | $55–70k | $80–110k |
| Back-end developer | APIs, databases, servers | $60–75k | $90–120k |
| Full-stack developer | Both front and back end | $65–80k | $95–130k |
| Web designer | UI/UX + HTML/CSS | $45–60k | $65–90k |
| WordPress developer | WordPress themes + plugins | $40–55k | $60–80k |
Common mistakes new web developers make
| Mistake | Why it's a problem | Better approach |
|---|---|---|
| Tutorial hell — watching without building | You don't learn by watching | Build something after every tutorial |
| Learning everything before building | Endless loop of "not ready yet" | Build with gaps in your knowledge |
| Not using version control | Lose work, can't collaborate | Commit every project from day one |
| Copying code without understanding it | Can't explain it in interviews | Type it out, then modify it |
| Skipping the basics (HTML, CSS) | Weak foundation shows in interviews | Master HTML/CSS before JavaScript |
| Learning multiple frameworks simultaneously | Shallow knowledge of all, expert in none | One framework at a time |
| Not deploying projects | Portfolio of local apps means nothing | Deploy everything |
| Ignoring accessibility | Excludes users, fails interviews | Add alt text, labels, semantic HTML |
Web developer vs related roles
| Role | Similarity | Key difference |
|---|---|---|
| Web developer | — | Builds websites and web apps |
| Software engineer | High | More general — can be desktop, embedded, systems |
| DevOps engineer | Medium | Focuses on infrastructure, CI/CD, cloud |
| Data engineer | Low | Focuses on data pipelines, ETL, warehouses |
| Mobile developer | Medium | Same languages sometimes, but native mobile UI |
| UX designer | Low | Designs user experience — not always coding |
Frequently asked questions
Do I need a degree to become a web developer? No. Hiring managers care about your portfolio, not your degree. Many successful developers are self-taught. A degree helps at large companies that filter by credential, but bootcamp grads and self-taught developers land jobs every day.
How long does it take to get a junior job? With full-time study: 9–12 months. With part-time study while working: 18–24 months. The biggest variable is the quality of your portfolio projects and how much you actively apply and network.
Should I learn TypeScript as a beginner? Not immediately. Learn JavaScript thoroughly first. After 3–4 months of JavaScript, add TypeScript — the payoff is huge (fewer bugs, better tooling, required at most companies for mid/senior roles).
Front end or back end first? Front end. The visual feedback keeps you motivated, you can show others what you built, and every job needs front-end skills. Once you have a foundation, back end clicks much faster.
Which framework should I learn — React, Vue, or Angular? React for maximum job opportunities. Vue if you find React's mental model confusing. Angular only if you specifically target enterprise roles. Don't learn all three — go deep on one.
What should I put in my portfolio? 2–3 real, deployed projects that solve an actual problem — not clone apps. Include source code on GitHub, a live link, and a README explaining what it does, why you built it, and what you learned. Quality beats quantity.