Theme: Building a makeup e-commerce platform called GlowShop Goal: Learn the fundamentals of web programming through real, practical challenges, covering everything from frontend to documentation. Audience: Beginners in tech and developers who want to reinforce their foundations.
Each challenge builds on the previous one. You are constructing GlowShop, a fictional online makeup store, piece by piece.
Every challenge covers a specific area of web development:
| # | Area | Difficulty |
|---|---|---|
| 1 | Frontend | π’ Easy |
| 2 | Backend | π‘ Medium |
| 3 | Database | π Intermediate |
| 4 | Software Testing | π΄ Advanced |
| 5 | UX & Documentation | π£ Expert |
You don't need to complete all challenges at once. Take your time, research, and experiment. The learning happens in the process.
This case study was inspired by Make Delas, a project by the He4rt Delas community: a group dedicated to supporting women and underrepresented people in tech. Make Delas aims to build a real web platform for researching and comparing makeup products, with a monolithic architecture, a modern stack, and a collaborative methodology.
GlowCode takes the spirit of that project (makeup as context, technology as the vehicle) and shapes it into a progressive learning environment. If you want to see how a real project is structured, with genuine architecture decisions, Git Flow, and collaborative contribution, go explore Make Delas. It is a beautiful example of what happens when a community decides to build something together.
π github.com/He4rt-Delas/make-delas
Before starting, bookmark these platforms. You will use them throughout every challenge. All three options below are 100% in Brazilian Portuguese and designed for anyone who is just starting out or wants to go deeper:
-
Curso em VΓdeo. Created by professor Gustavo Guanabara, this is one of the most beloved learning platforms in Brazil. It offers free and structured courses on HTML, CSS, JavaScript, Python, Git, and more, with clear video lessons, hands-on exercises, and certificates. Perfect for learning from absolute zero.
-
4Noobs. The 4noobs repository, part of the He4rt Developers community, brings together in one place open-source tutorials for beginners on a variety of programming technologies and topics, organized by category and functionality.
-
WoMakersCode - Cursos). The Mais Mulheres em Tech platform offers free online tracks to train women in fields such as cloud computing, infrastructure, information security, DevOps, development, data science, and artificial intelligence. Created by WoMakersCode in partnership with technology companies, the initiative brings together practical courses, mentorship, and programs focused on official certifications, featuring an accessible format, content in Portuguese, and a focus on inclusion and employability in the technology market.
GlowShop is a makeup e-commerce platform where users can:
- Browse products by category (lipstick, foundation, eyeshadow, etc.)
- View product details and reviews
- Add items to a cart and simulate a purchase
- Register and log in
You will build parts of this platform in each challenge.
Area: Frontend Difficulty: π’ Easy Language/Technology: HTML + CSS (optionally JavaScript for interaction)
The first thing a user sees on GlowShop is a list of makeup products. Your job is to build a product card, a reusable UI component that displays a product's image, name, category, price, and an "Add to Cart" button.
A single product card with:
- Product image (you can use a placeholder like
https://placehold.co/300x300) - Product name (e.g., Velvet Matte Lipstick)
- Category tag (e.g., Lips)
- Price (e.g., $24.99)
- An "Add to Cart" button
- A hover effect on the card
For this challenge, use HTML and CSS. If you want to go further, add a small JavaScript interaction (like toggling a "Added!" label on the button).
Why HTML + CSS? HTML defines the structure of your page. CSS controls how it looks. These are the absolute foundation of every website. No framework or library runs without them.
<!-- Example structure of a product card -->
<div class="product-card">
<img src="https://placehold.co/300x300/f8c8d4/fff?text=Lipstick" alt="Velvet Matte Lipstick" />
<span class="category-tag">Lips</span>
<h2 class="product-name">Velvet Matte Lipstick</h2>
<p class="product-price">$24.99</p>
<button class="add-to-cart">Add to Cart</button>
</div>/* Example base styling */
.product-card {
width: 300px;
border-radius: 12px;
padding: 16px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
transition: transform 0.2s ease;
}
.product-card:hover {
transform: translateY(-6px);
}
.category-tag {
background-color: #f8c8d4;
color: #a0416a;
padding: 4px 10px;
border-radius: 999px;
font-size: 12px;
}- Card displays image, name, category, price, and button
- Hover effect is visible
- The card is responsive (works on mobile screen widths)
- Code uses semantic HTML tags (
<img>,<h2>,<p>,<button>)
Use Google Fonts to pick a font that feels elegant. Try Playfair Display for the product name.
Area: Backend Difficulty: π‘ Medium Language/Technology: Node.js with Express (JavaScript) or Python with Flask
A real store doesn't hardcode its product list in HTML. Products come from a server, a program running in the background that responds to requests with data. Your job is to build a simple REST API for GlowShop's products.
A backend server with the following routes:
| Method | Route | Description |
|---|---|---|
GET |
/products |
Returns a list of all products |
GET |
/products/:id |
Returns a single product by ID |
POST |
/products |
Creates a new product |
Choose one of the options below. Pick the one you are most curious about:
Option A: Node.js + Express (JavaScript)
If you already know JavaScript from Challenge 1, this is a natural progression. Express is a minimal web framework that makes building APIs fast.
# Initialize a project and install Express
npm init -y
npm install express// server.js
const express = require('express');
const app = express();
app.use(express.json());
const products = [
{ id: 1, name: 'Velvet Matte Lipstick', category: 'Lips', price: 24.99 },
{ id: 2, name: 'Glow Foundation', category: 'Face', price: 39.99 },
];
app.get('/products', (req, res) => {
res.json(products);
});
app.get('/products/:id', (req, res) => {
const product = products.find(p => p.id === Number(req.params.id));
if (!product) return res.status(404).json({ error: 'Product not found' });
res.json(product);
});
app.post('/products', (req, res) => {
const newProduct = { id: products.length + 1, ...req.body };
products.push(newProduct);
res.status(201).json(newProduct);
});
app.listen(3000, () => console.log('GlowShop API running on port 3000'));Option B: Python + Flask
Flask is a lightweight Python web framework, and a great choice if you prefer Python.
pip install flask# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
products = [
{"id": 1, "name": "Velvet Matte Lipstick", "category": "Lips", "price": 24.99},
{"id": 2, "name": "Glow Foundation", "category": "Face", "price": 39.99},
]
@app.route("/products", methods=["GET"])
def get_products():
return jsonify(products)
@app.route("/products/<int:product_id>", methods=["GET"])
def get_product(product_id):
product = next((p for p in products if p["id"] == product_id), None)
if not product:
return jsonify({"error": "Product not found"}), 404
return jsonify(product)
@app.route("/products", methods=["POST"])
def create_product():
data = request.get_json()
new_product = {"id": len(products) + 1, **data}
products.append(new_product)
return jsonify(new_product), 201
if __name__ == "__main__":
app.run(debug=True)-
GET /productsreturns all products as JSON -
GET /products/:idreturns a 404 if the product doesn't exist -
POST /productscreates a product and returns it with a generated ID - Test your API using Insomnia or Postman (both free)
Think about what data a makeup product actually needs. Add fields like brand, shade, stock, and image_url to make it more realistic.
Area: Database Difficulty: π Intermediate Language/Technology: SQL (SQLite or PostgreSQL) + integration with your backend from Challenge 2
Right now, GlowShop's products live in memory, which means every time the server restarts, data is lost. A real application needs a database to persist information. In this challenge, you will replace the in-memory array with a relational database.
- A
productstable in a SQL database - A
categoriestable (lipstick, foundation, blush, etc.) - A
reviewstable linked to products - Updated API routes from Challenge 2 that read from and write to the database
Use SQL as the query language, and choose a database engine:
- SQLite: Stores data in a single file. Zero configuration. Perfect for learning.
- PostgreSQL: A production-grade relational database. More realistic, slightly more setup.
Recommended for beginners: Start with SQLite.
-- Schema definition
CREATE TABLE categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
price REAL NOT NULL,
stock INTEGER DEFAULT 0,
image_url TEXT,
category_id INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (category_id) REFERENCES categories(id)
);
CREATE TABLE reviews (
id INTEGER PRIMARY KEY AUTOINCREMENT,
product_id INTEGER NOT NULL,
author TEXT NOT NULL,
rating INTEGER CHECK(rating BETWEEN 1 AND 5),
comment TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (product_id) REFERENCES products(id)
);-- Seed data
INSERT INTO categories (name) VALUES ('Lips'), ('Eyes'), ('Face'), ('Skin');
INSERT INTO products (name, price, stock, category_id)
VALUES
('Velvet Matte Lipstick', 24.99, 50, 1),
('Smoky Eye Palette', 49.99, 30, 2),
('Glow Foundation', 39.99, 25, 3);Connecting to Node.js (using better-sqlite3):
npm install better-sqlite3const Database = require('better-sqlite3');
const db = new Database('glowshop.db');
app.get('/products', (req, res) => {
const products = db.prepare(`
SELECT p.*, c.name AS category
FROM products p
JOIN categories c ON p.category_id = c.id
`).all();
res.json(products);
});- Database schema created with all three tables
- Foreign key relationships are correctly defined
- Seed data is present (at least 5 products, 3 categories)
- All API routes from Challenge 2 now use the database instead of a hardcoded array
-
GET /products/:idalso returns the category name and average rating
Use DB Browser for SQLite (free) to visually inspect your database tables. It's a great way to understand what your queries are actually doing.
Area: Software Testing Difficulty: π΄ Advanced Language/Technology: Jest (JavaScript) or Pytest (Python), matching whichever backend you chose
Code that isn't tested is code you can't trust. In this challenge, you will write automated tests for GlowShop's API. You'll cover unit tests (testing isolated functions) and integration tests (testing how the API routes behave end-to-end).
A test suite covering:
- Unit tests: Test a function that calculates a discount price
- Integration tests: Test each API route (
GET,POST, error cases) - Edge case tests: What happens with invalid input? Missing fields? Negative prices?
Option A: Jest (Node.js/JavaScript)
npm install --save-dev jest supertest// utils/discount.js
function applyDiscount(price, percentOff) {
if (typeof price !== 'number' || price < 0) throw new Error('Invalid price');
if (percentOff < 0 || percentOff > 100) throw new Error('Invalid discount');
return parseFloat((price * (1 - percentOff / 100)).toFixed(2));
}
module.exports = { applyDiscount };// __tests__/discount.test.js
const { applyDiscount } = require('../utils/discount');
describe('applyDiscount()', () => {
test('applies 20% off correctly', () => {
expect(applyDiscount(24.99, 20)).toBe(19.99);
});
test('returns full price for 0% discount', () => {
expect(applyDiscount(24.99, 0)).toBe(24.99);
});
test('throws for negative price', () => {
expect(() => applyDiscount(-10, 10)).toThrow('Invalid price');
});
test('throws for discount over 100%', () => {
expect(() => applyDiscount(24.99, 150)).toThrow('Invalid discount');
});
});// __tests__/products.test.js
const request = require('supertest');
const app = require('../server');
describe('GET /products', () => {
test('returns 200 and an array', async () => {
const res = await request(app).get('/products');
expect(res.statusCode).toBe(200);
expect(Array.isArray(res.body)).toBe(true);
});
});
describe('GET /products/:id', () => {
test('returns 404 for unknown product', async () => {
const res = await request(app).get('/products/9999');
expect(res.statusCode).toBe(404);
});
});
describe('POST /products', () => {
test('creates a product and returns 201', async () => {
const res = await request(app)
.post('/products')
.send({ name: 'Glitter Blush', price: 18.99, category_id: 3 });
expect(res.statusCode).toBe(201);
expect(res.body).toHaveProperty('id');
expect(res.body.name).toBe('Glitter Blush');
});
test('rejects request with missing price', async () => {
const res = await request(app)
.post('/products')
.send({ name: 'No Price Product' });
expect(res.statusCode).toBe(400);
});
});Option B: Pytest (Python)
pip install pytest httpx# test_api.py
import pytest
from app import app
@pytest.fixture
def client():
app.config["TESTING"] = True
with app.test_client() as client:
yield client
def test_get_all_products(client):
res = client.get("/products")
assert res.status_code == 200
assert isinstance(res.get_json(), list)
def test_get_unknown_product(client):
res = client.get("/products/9999")
assert res.status_code == 404
def test_create_product(client):
payload = {"name": "Glitter Blush", "price": 18.99, "category_id": 3}
res = client.post("/products", json=payload)
assert res.status_code == 201
assert res.get_json()["name"] == "Glitter Blush"- At least 8 tests written total
- Unit tests cover the discount utility with happy path AND edge cases
- Integration tests cover all three routes (
GET /products,GET /products/:id,POST /products) - All tests pass when running
npm testorpytest - At least one test intentionally checks for a failure scenario (404, 400, invalid input)
Think of tests as documentation. A well-named test like "returns 404 for unknown product" tells the next developer exactly what the system is supposed to do.
Area: UX Design + Technical Documentation Difficulty: π£ Expert Language/Technology: Figma (free tier) for UX, Markdown for documentation
A platform is only as good as the experience it creates. In this final challenge, you will step back from code and think about people: how they navigate, what confuses them, and how to communicate clearly. You will also write complete documentation for the GlowShop API.
Map the full user journey for a customer purchasing a lipstick on GlowShop:
[Homepage] β [Product Listing] β [Product Detail Page] β [Cart] β [Checkout] β [Confirmation]
For each screen, define:
- What is the user trying to accomplish?
- What are the 3 most important elements on this screen?
- What could go wrong or confuse the user here?
Wireframe at least 3 screens using Figma (free) or even pen and paper (photo is fine). Focus on layout and hierarchy, not visual design.
UX Heuristics to apply (pick at least 3):
| Heuristic | What to check in GlowShop |
|---|---|
| Visibility of system status | Does the cart show a count? Is the "adding" state shown? |
| Error prevention | Can a user accidentally submit an empty cart? |
| Recognition over recall | Are categories visible without needing to search? |
| Aesthetic & minimalist design | Is there unnecessary information cluttering the page? |
| Help & documentation | Is there a way to contact support or find answers? |
Write a complete API.md file that another developer can use to integrate with your API without asking you a single question.
Your documentation must include:
# GlowShop API Reference
## Base URL
http://localhost:3000
## Authentication
(Describe if any auth is required, or explicitly state "No authentication required for this version.")
## Endpoints
### GET /products
Returns a list of all products.
**Response 200 OK**
\`\`\`json
[
{
"id": 1,
"name": "Velvet Matte Lipstick",
"category": "Lips",
"price": 24.99,
"stock": 50
}
]
\`\`\`
### POST /products
Creates a new product.
**Request Body**
\`\`\`json
{
"name": "Glitter Blush",
"price": 18.99,
"category_id": 3,
"stock": 20
}
\`\`\`
**Response 201 Created**
\`\`\`json
{
"id": 4,
"name": "Glitter Blush",
"price": 18.99,
"category": "Face",
"stock": 20
}
\`\`\`
**Error Responses**
| Code | Reason |
|------|--------|
| 400 | Missing required fields |
| 404 | Product not found |
| 500 | Internal server error |- User flow covers at least 3 screens with annotations
- At least 3 UX heuristics are applied and explained
-
API.mdcovers all routes with request/response examples - Documentation includes an error table for each endpoint
- README.md in your repo explains how to run the project locally (from zero)
If you are going through this case study, you are already doing the work. Make it count publicly.
π 100diasdecodigo.dev is a Portuguese-language initiative based on the global #100DaysOfCode movement. The rules are simple:
- Code for at least 1 hour every day for 100 days
- Tweet or post your progress using #100DaysOfCode and #100DiasDeCodigo
- Encourage at least 2 other people each day
How GlowCode fits in: Each challenge in this case study is roughly 1β3 days of honest work. Document your journey publicly:
- Share your product card on day 1
- Share your first API response on day 4
- Share your first passing test on day 7
The community is your accountability. Show up.
This bonus is for everyone who clones this repository. It turns GlowCode from a coding exercise into a portfolio piece.
When you clone this repo and complete the challenges, you are expected to do the following in addition to the code:
For every challenge, create a file called DECISIONS.md inside each challenge folder. In it, explain:
# Challenge X: Decision Log
## Technology Chosen
Why did I pick Node.js over Python? Why SQLite over PostgreSQL?
## Architecture Decisions
Why did I structure the project this way?
## What I Would Do Differently
If I had more time or knew more, what would I change?
## Hardest Part
What took the longest? What confused me the most?There are no wrong answers here. The goal is thinking out loud.
Create a section called AI_USAGE.md in the root of your repository. Use this template:
# AI Usage Transparency Log
## Where I Used AI
- Challenge 1: Used GitHub Copilot to autocomplete the CSS hover effect
- Challenge 3: Asked ChatGPT to explain what a FOREIGN KEY is
- Challenge 4: Used Claude to review my test names for clarity
## Where I Did NOT Use AI
- Challenge 1: Wrote the full HTML structure myself
- Challenge 2: Debugged the 404 response logic without AI help
- Challenge 5: Designed the user flow entirely on my own
## What I Learned From NOT Using AI
(Describe one thing that you understood deeply because you struggled through it yourself)
## Reflection
Using AI to understand something is different from using AI to skip understanding it.
Which one did you do?There is no judgment here. This is about self-awareness, not purity. AI is a tool. Know how you are using it.
All your DECISIONS.md, AI_USAGE.md, API.md, and README.md files must be written in English.
Why?
- The global tech community communicates in English
- Your GitHub profile is your international portfolio
- Writing technical explanations in English is a skill that compounds over time
Your code comments, commit messages, and pull request descriptions should also be in English.
Tip: If you have trouble at first, that's okayβuse a translator. Here's a really good one: https://www.deepl.com/
-
DECISIONS.mdexists in each challenge folder with genuine reasoning -
AI_USAGE.mdis honest, specific, and reflective (not generic) - All documentation files are written in English
- Your
README.mdtells a stranger how to run the project in under 5 minutes - Commit messages are descriptive and in English (e.g.,
feat: add product listing endpointnotupdate stuff)
GlowCode is not about makeup.
It's about building something real, being honest about your process, and showing your thinking to the world. The best developers are not the ones who know everything. They are the ones who document what they don't know yet, ask good questions, and keep showing up.
Now clone this repo, open your editor, and build something beautiful.
Made with π and curiosity. Fork it, break it, make it yours.
glowcode/
βββ README.md
βββ AI_USAGE.md
βββ challenge-1-frontend/
β βββ index.html
β βββ style.css
β βββ DECISIONS.md
βββ challenge-2-backend/
β βββ server.js (or app.py)
β βββ package.json (or requirements.txt)
β βββ DECISIONS.md
βββ challenge-3-database/
β βββ schema.sql
β βββ seed.sql
β βββ server.js (or app.py, updated)
β βββ DECISIONS.md
βββ challenge-4-testing/
β βββ __tests__/ (or tests/)
β βββ utils/
β βββ DECISIONS.md
βββ challenge-5-ux-docs/
βββ wireframes/ (images or Figma link)
βββ API.md
βββ DECISIONS.md