Broken Full-Stack Feature Lab
Before you start
You need: all of Articles 01–06, and a working full-stack feature to break.
Best done in pairs — one plants the defects, the other debugs, then swap.
Time: 6–10 hours.
Goal
Take a working full-stack feature, plant eight specific defects, and find each one using the method rather than by remembering where you put it.
Assignment
You will break the exam results feature of the School Management System in eight ways, then debug each as though you had never seen it.
Have someone else plant them if you can. A partner introduces the defects, you debug; then swap. Debugging a bug you planted tests your memory, not your method.
Working alone, plant all eight, wait a day, then shuffle the order and work through them by symptom.
For every defect, produce a written report with reproduction, evidence at each boundary, the cause, the fix, and a test. The report is the deliverable — the fixes are the easy part.
The feature
React results page → GET /api/exams/{examId}/results → ExamResultService
→ ExamResultRepository → SQL Server
It should show, for one exam: every student in the class, their marks, and Pass, Fail or Absent.
SELECT
s.Id, s.Name, s.RollNumber,
r.MarksObtained, r.IsAbsent,
CASE WHEN r.IsAbsent = 1 THEN 'Absent'
WHEN r.MarksObtained >= sub.PassingMarks THEN 'Pass'
ELSE 'Fail' END AS Result
FROM ExamResult r
JOIN Student s ON s.Id = r.StudentId
JOIN Subject sub ON sub.Id = r.SubjectId
WHERE r.ExamId = @ExamId
AND r.SchoolId = @SchoolId
AND s.IsDeleted = 0
ORDER BY s.RollNumber;
The eight defects
1. Frontend — render before data
useEffect(() => {
loadResults(examId);
setLoading(false);
}, [examId]);
Symptom: the page shows "No results" briefly, or permanently on a slow connection.
Where the trace should stop: Network shows a 200 with correct data. The frontend is at fault.
2. Network — missing Content-Type
Remove the header from the POST that saves marks.
Symptom: 415, with a message that does not mention the header.
Where the trace should stop: the request headers in the Network panel.
3. API — SchoolId from the request
public async Task<IActionResult> GetResults(int examId, [FromQuery] int schoolId)
Symptom: none. Every response is a 200 with plausible data.
How to find it: sign in as School 1 and request ?schoolId=2. This defect has no error, no log line and no failing test. It is a data breach.
4. API — middleware order
Swap UseAuthentication and UseAuthorization.
Symptom: 401 on every request, including with a valid token.
Where the trace should stop: the token decodes correctly, so the fault is the pipeline, not the token.
5. Service — absent check last
if (result.MarksObtained >= subject.PassingMarks) { return "Pass"; }
if (result.IsAbsent) { return "Absent"; }
return "Fail";
Symptom: absent students show as Fail. No exception. The page looks entirely normal.
How to find it: compare one absent student's row against the database. The value is wrong; nothing else is.
6. Repository — inner join drops rows
Change the Student join so students without an ExamResult row disappear, and confirm the class count drops from 40 to 37.
Symptom: three students missing from the report.
How to find it: compare row counts. SELECT COUNT(*) FROM Student WHERE ClassName='10th' against the API's result count.
7. Database — absent marks stored as 0
Update the seed data so absent students have MarksObtained = 0, IsAbsent = 1.
Symptom: the class average is wrong. Every individual row looks right.
How to find it: compute the average by hand for five students and compare. AVG includes the zeros.
8. Full flow — cache key without SchoolId
string key = $"results:{examId}";
Symptom: intermittent. School B occasionally sees School A's results, only when both request the same exam id within the cache window.
How to find it: it is correct after a restart. That is the signature.
Working through them
For each defect, in order:
1. Reproduce. Write the steps, the expected result and the actual result. If you cannot reproduce it on demand, you cannot verify the fix.
2. Check the boundaries. Browser render → network response → API log → SQL → data. Record whether the data was correct at each. Stop at the first boundary where it is wrong.
3. State a hypothesis and test it. Write both down, including the ones you reject.
4. Fix. The smallest change that addresses the cause.
5. Verify. Reproduce the original steps and confirm the correct behaviour. Then check that the other seven still behave as before.
6. Add a test that fails before the fix and passes after.
Report template
Write one of these per defect.
Defect N: <one-line symptom>
Reproduction
Steps:
Expected:
Actual:
Frequency and scope (all users / one record / one environment):
Boundary trace
Browser render matches response? yes / no
Network status and response body:
API log — SQL and parameters:
Same SQL run in SSMS:
Does the data support the result?
First boundary where the data was wrong:
Hypotheses
Tested: Result:
Rejected: Why:
Cause
What the code does:
Why it produces this symptom:
Why no error appeared (if none did):
Fix
Change made:
Why this and not a larger change:
Verification
Original steps reproduce correctly:
Other seven defects unaffected:
Test added (name, and what it asserts):
Pattern
Does this bug exist elsewhere in the codebase?
Verification
Every defect has a written report. Eight reports, each identifying the first bad boundary.
Every fix has a test that fails without it. Comment out the fix, run the test, watch it fail, restore. A test that passes either way tests nothing.
Defects 3, 5, 7 and 8 are identified without any error message. These four are the point of the lab: nothing throws, nothing logs, and the application looks fine.
Defect 3 is demonstrated as a breach. Show the request from School 1 returning School 2's data, and the fix reading the claim.
Defect 7 is proved by arithmetic. Compute the average by hand for five students, before and after.
Defect 8 is proved by a restart. Show the value correct after a restart and wrong before.
No defect was found by remembering. If you skipped the trace, redo it — the method is what you are practising.
Every fix is minimal. Reverting all eight should return the file to its original state, with no unrelated change.
AI practice
Three AI exercises from this track's syllabus. Do each after all eight defects are found, and apply Track 18's discipline — every answer is a hypothesis until you have run it.
- Ask AI to guide investigation step by step. Describe defect 8 — the intermittent cross-school cache — and ask what to check next, one step at a time. Do not paste the code. Compare its suggested order against the boundary trace you actually used.
- Do not allow an immediate full rewrite. When it offers corrected code, decline and ask for the cause instead. A rewrite fixes the instance and teaches nothing; for defects 3, 5, 7 and 8 the cause is the entire lesson, because none of them produce an error message.
- Compare AI hypotheses with debugger evidence. For defect 5 — absent students marked Fail — record the causes it proposes, then test each with a conditional breakpoint. Note which were right, which were plausible and wrong, and which you had already ruled out. Telling it what you have ruled out is the single line that most improves the answer.
The pattern across all three: AI is a second opinion on evidence you have gathered, not a substitute for gathering it. Ask before reproducing and you get a confident answer about a different bug.
Track 18 — Reviewing AI-generated code — has the full checklist.
Self-assessment
Your submission is complete when eight reports each show a trace, a hypothesis and a test.
Five specific tests of quality:
- Which four defects produce no error? 3, 5, 7 and 8. If you can say why each is silent, you understand the category that matters most in production.
- Did the trace stop at the right boundary each time? Defect 1 stops at the response; defect 6 stops at the SQL. If every trace went all the way to the database, you were reading code rather than testing boundaries.
- Did you record rejected hypotheses? A hypothesis rejected is a possibility eliminated. Without the record, you will retest it.
- Would your tests catch a regression? Defect 5's test needs an absent student. Defect 3's needs two schools. A happy-path test catches neither.
- Does defect 3 exist anywhere else? Search every controller for a
schoolIdparameter. The pattern is the finding, not the instance.
Track completion criteria
You can debug systematically instead of guessing, using the debugger, logs, browser tools and SQL as evidence.
Specifically, you can:
- Turn "it doesn't work" into a reproducible report
- Read an error message and a stack trace for what they state
- Form a hypothesis and design a test that could reject it
- Use conditional breakpoints, Watch, the Call Stack and first-chance exceptions
- Find a swallowed exception's throw site
- Decide from the Network panel whether a failure is frontend or backend
- Read status codes correctly, including 401 versus 403
- Diagnose CORS, storage and token-expiry problems
- Isolate an API failure in Postman and read the server log
- Diagnose model binding, validation and middleware-order failures
- Capture the SQL your application runs and verify it against the data
- Recognise the silent data bugs: missing tenant filter, missing soft-delete filter, absent stored as zero, a join that drops or multiplies rows
- Diagnose slowness with logical reads and an execution plan
- Find blocking with
sys.dm_exec_requests - Trace a request across four layers and name the failing boundary
- Recognise environment, timing and cache causes from their signatures
- Write up a bug so the next person recognises the pattern
The syllabus recommends Track 18 — Claude & Codex AI-Assisted Development or Track 01 — Microsoft .NET Full Stack Guided Path next.