Before this course: Full-Stack Basics: servers, databases and deployment (course 7.7.3). You should already have built and deployed "Study Notes": a Node.js JSON API with input checks and a body size limit, a SQLite database with prepared statements, scrypt password hashing, session cookies with HttpOnly and SameSite, ownership checks on every note, 9 automated tests, and settings in environment variables. Now you stop being the only developer. In this course you design, secure, test and run an app the way a professional team does, and you learn to teach those habits to others.
Architecture you can explain and defend
A house has rooms with different jobs: a kitchen, a bedroom, a storeroom. Some doors are locked, and the front door is where strangers come in. A good architect can point at the plan and say what each room is for and where every lock is. A good app has a plan like that too.
- Layers keep each part of the code to one job:
- The HTTP layer (routes) reads requests and writes responses: status codes, headers, JSON.
- The service layer holds the rules: what a valid note is, who may delete it.
- The data layer (a repository) talks to the database and nothing else.
- Pass dependencies in instead of creating them inside. In course 7.7.3,
createApp({ db })let tests use an in-memory database. The same idea at the service layer lets tests use a fake repository, so the rules can be tested in milliseconds with no database at all. - An architecture decision record (ADR) is a short note a team keeps for each important choice: the context, the decision and its consequences. New people can then see why the code looks the way it does.
- A trust boundary is a place where data moves between parts you trust differently: browser to server, server to database, your server to another company's API. Every boundary needs checks.
- OWASP Top 10 (2025) includes A06 Insecure Design. OWASP says design flaws and coding bugs have different root causes and different fixes, and that "an insecure design cannot be fixed by a perfect implementation".
- Threat modeling (OWASP Threat Modeling Cheat Sheet) asks four questions: "What are we working on?", "What can go wrong?", "What are we going to do about it?" and "Did we do a good enough job?"
- STRIDE is a checklist of six kinds of threat, each breaking one security property:
| Threat | Breaks | Study Notes example | Our defence |
|---|---|---|---|
| Spoofing | Authentication | Someone logs in as another user | Slow salted hashes, login throttle (Lesson 4) |
| Tampering | Integrity | A request changes someone else's note | Ownership check in the service layer |
| Repudiation | Accounting | "I never deleted that note" | Security log of who did what (Lesson 7) |
| Information disclosure | Confidentiality | Notes or errors leak to the wrong person | 404 for others' notes, generic errors |
| Denial of service | Availability | Huge requests or endless login tries | Body size limit, throttling |
| Elevation of privilege | Authorization | A normal user reaches admin actions | Deny by default, checks on the server |
- A data flow diagram shows the processes, data stores, external entities, data flows and trust boundaries. OWASP says threat modeling is best done early, during design, and kept up to date as the system changes.
// Service layer: business rules only. No HTTP, no SQL.
// The repository (data layer) is passed in, so tests can use a fake one.
function createNotesService({ repo }) {
function cleanText(text) {
if (typeof text !== "string") return null;
const t = text.trim();
if (t.length < 1 || t.length > 2000) return null;
return t;
}
return {
list(userId) {
return repo.findByOwner(userId);
},
create(userId, text) {
const t = cleanText(text);
if (!t) return { ok: false, error: "Note must be 1 to 2000 characters." };
return { ok: true, note: repo.insert(userId, t) };
},
remove(userId, noteId) {
// Ownership is checked here, once, for every caller.
return repo.deleteOwned(userId, noteId) === 1;
},
};
}
// A tiny in-memory repository with the same methods as the SQL one.
function memoryRepo() {
const rows = []; let nextId = 1;
return {
findByOwner: (u) => rows.filter((r) => r.userId === u),
insert(u, text) { const r = { id: nextId++, userId: u, text }; rows.push(r); return r; },
deleteOwned(u, id) {
const i = rows.findIndex((r) => r.id === id && r.userId === u);
if (i === -1) return 0;
rows.splice(i, 1); return 1;
},
};
}
module.exports = { createNotesService, memoryRepo };
const test = require("node:test");
const assert = require("node:assert");
const { createNotesService, memoryRepo } = require("../src/notes-service");
test("creates a note for its owner", () => {
const s = createNotesService({ repo: memoryRepo() });
const r = s.create(1, " buy milk ");
assert.strictEqual(r.ok, true);
assert.strictEqual(r.note.text, "buy milk");
assert.strictEqual(s.list(1).length, 1);
});
test("rejects empty and huge notes", () => {
const s = createNotesService({ repo: memoryRepo() });
assert.strictEqual(s.create(1, " ").ok, false);
assert.strictEqual(s.create(1, "x".repeat(2001)).ok, false);
assert.strictEqual(s.create(1, 42).ok, false);
});
test("a user cannot delete someone else's note", () => {
const s = createNotesService({ repo: memoryRepo() });
const n = s.create(1, "mine").note;
assert.strictEqual(s.remove(2, n.id), false);
assert.strictEqual(s.list(1).length, 1);
assert.strictEqual(s.remove(1, n.id), true);
assert.strictEqual(s.list(1).length, 0);
});
Test result: all 3 tests passed. The HTTP layer from course 7.7.3 now only calls service.remove(user.id, id) and turns false into a 404. The ownership rule lives in one place.
- Draw Study Notes as boxes: Browser, Node.js server, SQLite file. Add arrows for each data flow (sign-up, login, list notes, delete note).
- Draw a dashed line at every trust boundary. Check: there is at least one between the browser and the server, and one between the server and the database.
- Create
src/notes-service.jsandtest/notes-service.test.jsfrom the example, then runnode --test. You should see 3 passing tests. - With one or two classmates, ask the four threat modeling questions out loud, one at a time.
- Fill in a STRIDE table for your own app: one real example and one defence for each letter.
- Write one ADR (half a page): "We keep ownership checks in the service layer", with context, decision and consequences.
- Check: someone new can read your diagram and ADR and explain where the ownership rule lives, without opening the code.
Videos by other creators on YouTube, for reference. They are not made by or affiliated with The Future Market.
Read the video script
[Host] "A good architect can point at the plan and say what every room is for. Can you do that for your app?"
[Screen: three boxes labelled HTTP layer, Service layer, Data layer] "Routes talk HTTP. The service holds the rules. The repository talks to the database. One job each."
[Screen: dashed trust boundary between browser and server] "Draw the trust boundaries. Every line where data crosses needs a check."
[Text on screen: What are we working on? What can go wrong? What will we do? Did we do enough?] "Then ask the four threat modeling questions, early, with your team."
[Text on screen: An insecure design cannot be fixed by a perfect implementation] "Because perfect code can't save a broken design."
Run a 30-minute threat modeling session on your own app with at least one other person. Produce a data flow diagram with trust boundaries, a STRIDE table with six rows and three new tasks for your backlog.
ClaudeStuck? Copy a ready-made prompt for Claude
Copy this, open Claude, paste it, and Claude will walk you through the lesson step by step.
For a grown-up: Claude is for adults (18+). Copy this, paste it into Claude, and use the answer to explain the lesson to your child.
I'm learning "Web Engineering: architecture, security and team practice" on The Future Market. Lesson 1: "Architecture you can explain and defend".
What the lesson teaches:
- Layers keep each part of the code to one job:
- The HTTP layer (routes) reads requests and writes responses: status codes, headers, JSON.
- The service layer holds the rules: what a valid note is, who may delete it.
- The data layer (a repository) talks to the database and nothing else.
- Pass dependencies in instead of creating them inside. In course 7.7.3, createApp({ db }) let tests use an in-memory database. The same idea at the service layer lets tests use a fake repository, so the rules can be tested in milliseconds with no database at all.
- An architecture decision record (ADR) is a short note a team keeps for each important choice: the context, the decision and its consequences. New people can then see why the code looks the way it does.
- A trust boundary is a place where data moves between parts you trust differently: browser to server, server to database, your server to another company's API. Every boundary needs checks.
The exercise I'm working on:
Run a 30-minute threat modeling session on your own app with at least one other person. Produce a data flow diagram with trust boundaries, a STRIDE table with six rows and three new tasks for your backlog.
I'm stuck. Please:
1. Explain the main idea in plain, simple words with one everyday example.
2. Walk me through the exercise one small step at a time. Ask me what I've done so far before giving the next step.
3. Don't just give me the final answer. Help me do it myself, and check my work when I share it.I'm a parent helping my child (about 8 years old) with a lesson called "Architecture you can explain and defend" from the course "Web Engineering: architecture, security and team practice". Here's what the lesson says: A house has rooms with different jobs: a kitchen, a bedroom, a storeroom. Some doors are locked, and the front door is where strangers come in. A good architect can point at the plan and say what each room is for and where every lock is. A good app has a plan like that too. The activity: Run a 30-minute threat modeling session on your own app with at least one other person. Produce a data flow diagram with trust boundaries, a STRIDE table with six rows and three new tasks for your backlog. Please explain this so an 8-year-old understands: short sentences, a fun everyday example, and no hard words. Then give me 3 simple questions I can ask my child to check they understood, and a way to do the activity together, one tiny step at a time.
Sources
- OWASP Cheat Sheet Series, Threat Modeling: cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.htm
- OWASP Top 10 (2025), A06 Insecure Design: top10.owasp.org/2025/A06_2025-Insecure_Design/
- Node.js documentation, test runner: nodejs.org/docs/latest-v22.x/api/test.html
A written security standard for your team
This lesson is part of full access: every course and every level for one payment.
Harden the browser side
This lesson is part of full access: every course and every level for one payment.
Test like a team: edge cases, time and gates
This lesson is part of full access: every course and every level for one payment.
Accessibility as an engineering standard
This lesson is part of full access: every course and every level for one payment.
Supply chain, review rules and secure code review
This lesson is part of full access: every course and every level for one payment.
Run it safely: logs, errors, reports and breaches
This lesson is part of full access: every course and every level for one payment.
Lead the team: standards, reviews and teaching
This lesson is part of full access: every course and every level for one payment.
Final project
This lesson is part of full access: every course and every level for one payment.
Quiz
What are the three layers used in this course, and what does each do?
The HTTP layer handles requests and responses, the service layer holds the rules, and the data layer talks to the database.
What is a trust boundary?
A place where data moves between parts you trust differently, such as browser to server; every boundary needs checks.
What are the four threat modeling questions?
What are we working on? What can go wrong? What are we going to do about it? Did we do a good enough job?
Which ASVS level does OWASP say most applications should aim for?
Level 2.
Name the four practice groups of the NIST SSDF.
Prepare the Organization, Protect the Software, Produce Well-Secured Software, Respond to Vulnerabilities.
What is the main defence against cross-site scripting, and what is CSP's role?
Encoding output for its context (or safe sinks like textContent); CSP is defence in depth.
Why roll out a new CSP with Content-Security-Policy-Report-Only first?
It reports problems without blocking anything, so you can fix them before you enforce.
Why should the login failure counter belong to the account and not the IP address?
So an attacker cannot reset it by changing addresses.
What does a coverage gate do, and what can it not tell you?
It fails the run when coverage drops below a threshold; it shows what code ran, not whether it was checked well.
Why should a login form allow paste and password managers?
WCAG 2.2 Accessible Authentication names them as mechanisms that help, and they support strong passwords.
What should you do if a real secret was committed to Git?
Remove it and rotate it: make a new secret and cancel the old one.
Which four things should every security log event record, and what must it never contain?
When, where, who and what; never passwords, session IDs, tokens, keys or card data.
Final exam
This lesson is part of full access: every course and every level for one payment.
Certificate
This lesson is part of full access: every course and every level for one payment.