Capstone Preparation
What can a Christian engineer build?
Chapter 15 — Capstone Preparation
“For which of you, desiring to build a tower, does not first sit down and count the cost, whether he has enough to complete it?” — Luke 14:28
“You don’t rise to the level of your expectations; you fall to the level of your training.” — Archilochus, by way of every coach on Earth
Why This Matters
The final is next week.
If you have done the reps in Chapters 1–14 — every comprehension brief, every spec, every test-first calculator, every sabotage recovery, every catechism pipeline, every recursive walk, every catalog midterm, every spec-AI-review, every iterative refinement project — Project 14 is going to feel like another project. One you have done many smaller versions of, in many small variations, all term.
If you have not, this chapter is the same warning Chapter 8 was for the midterm. The reps are the work. There is no substitute. The exam room is honest in a way the rest of the semester usually isn’t.
This chapter does three things:
- Compresses everything you’ve learned in Coding 2 into a single readable reference — how the fourteen skills fit together to build one small thing well.
- Walks through a worked example end-to-end so you can see all the skills firing in one program.
- Gives you a study plan for the week — full 7-day version plus a compressed 3-day fallback if you read this chapter four days before the exam.
There is no new project this week. The exercises file is Capstone Drills — practice runs in the shape of the final.
Use the week well.
15.1 — The Course, in One Paragraph
Coding 2 has been teaching one thing in fourteen distinct forms.
The thing: building a small system well, when one of your tools — but not the only one — can write code.
The fourteen skills that follow from that:
- Read the code carefully (Ch 1).
- Spec before you build (Ch 2).
- Handle errors deliberately (Ch 3).
- Test first, then trust the tests (Ch 4).
- Debug by hypothesis (Ch 5).
- Persist data carefully — files are the steward of truth (Ch 6).
- Recurse when the problem is self-similar (Ch 7).
- Reach for collections (
Map,Set,List) as native vocabulary (Ch 8). - Pair with AI as senior to junior (Ch 9).
- Prompt with the precision of a specification (Ch 10).
- Review AI’s code with discernment (Ch 11).
- Architect the system before you prompt the modules (Ch 12).
- Refine iteratively when the first answer is wrong (Ch 13).
- Author honestly — claim what is yours, attribute what isn’t (Ch 14).
Look at the list. Notice the shape. Skills 1–8 are senior engineering, no AI required. Skills 9–14 are senior engineering, with AI as a junior partner. Skills 1–8 do not become obsolete because of skills 9–14. They become the precondition for skills 9–14. That is the course’s thesis, restated.
Project 14 asks you to use all fourteen, in one small application, in 60 minutes.
Coach’s Note — Every project in Phase 2 used a subset of these skills. P9 was spec + AI + review. P10 was prompting. P11 was review. P12 was architecture. P13 was iterative refinement. The final asks for all of them, weighted toward whichever skills your specific scope needs most. The skills are the same; the integration is the new work.
15.2 — How the Skills Fit Together (The Build Order)
When you sit down to build a small application from scratch, the skills come into play in a specific order. Not the order they were taught. The order they’re useful.
Here is the order:
-
Spec (Ch 2). Decide what the program should do. Write the contract. Pre/post-conditions, invariants, error cases. This is the only step that is purely senior thinking. AI cannot do this for you, because the AI does not know what you are trying to accomplish.
-
Architecture (Ch 12). Decide the shape of the system. Which classes, what each one owns, where the boundaries are, what the public APIs look like. Draw it on paper. Again — AI cannot decide this for you, because the AI is not making the long-term tradeoffs you are.
-
Tests, before code (Ch 4). Write JUnit tests for the public API you specified. The tests are the executable version of the spec. They will catch you when the implementation drifts.
-
Persistence shape (Ch 6). Decide what data file the application reads from and writes to, and the format. Decide where errors in the file are handled (Ch 3). The persistence layer is the boundary where your program meets the messy real world.
-
Module-by-module implementation, AI on (Ch 9, 10, 13). Now the AI helps. You prompt for one method at a time, scoped, with the test already written. Iterative refinement (Ch 13) gets you to green for each method.
-
Review (Ch 11). After the AI produces a method, you read it. Bugs, hallucinated APIs, off-by-ones, wrong null handling — all the patterns from Ch 11.
-
Integration test (Ch 4 again). Run the full suite. Run the application end-to-end. Confirm the persistence round-trips. Confirm the error cases fail informatively.
-
Reflection (Ch 14). Write down what the AI did, what you did, what you redirected, what you hand-typed. Honest. Specific.
That is the integrated workflow. Project 14 asks you to execute it in 60 minutes. The reason 60 minutes is possible is that all the skills are already in your hands; you are not learning anything new during the exam, you are just sequencing what you know.
15.3 — Worked Example: A 30-Minute Mini-Capstone
Let’s walk one through. We’ll build a small piece of what could be a real Project 14 submission — not the whole thing, but enough to show every skill firing.
Pretend prompt: “Build a small daily-verse rotator. Each day, present one verse from a CSV file. Track which verses have been shown so each verse appears once before any verse repeats.”
Step 1 — Spec (3 minutes)
Open a notes file. Write down:
DailyVerseRotator
=================
Inputs:
- A CSV file at a known path, format: "reference,text"
Example row: "Psalm 23:1,The LORD is my shepherd; I shall not want."
- A state file (JSON or txt) that tracks which verses have been shown.
Outputs:
- The reference + text of today's verse, printed to stdout.
Invariants:
- Each verse is shown at most once per "cycle" through the CSV.
- When all verses have been shown, the cycle resets and shuffles again.
- If the CSV is empty or missing, fail informatively (not silently).
Public API:
- DailyVerseRotator(Path csv, Path state)
- Verse next() — returns today's verse, advances state
- void resetCycle() — clears the shown-set, starts a new cycle
- int versesRemaining() — how many verses left in current cycle
Notice what’s here:
- Inputs and outputs are explicit.
- Invariants are explicit.
- The public API is explicit.
That’s the spec. It took three minutes. The rest of the build will refer back to it.
Step 2 — Architecture (3 minutes)
Draw on paper (or pseudocode):
Three classes:
1. Verse — a data class.
Fields: String reference, String text.
Constructor + getters.
2. VerseLoader — loads the CSV.
Method: List<Verse> loadAll(Path csv) throws IOException
Handles: missing file, empty file, malformed rows.
3. DailyVerseRotator — the main logic.
Owns: List<Verse> allVerses, Set<String> shownReferences, Path stateFile.
Methods: next(), resetCycle(), versesRemaining().
Persists shownReferences to stateFile after every next().
Three classes, single responsibility each. Verse is data. VerseLoader handles I/O. DailyVerseRotator is the state machine.
That’s the architecture. Three minutes. Drawn before any code.
Step 3 — Tests, before code (8 minutes)
Open DailyVerseRotatorTest.java. Write six tests, focusing on the most important behaviors:
@Test
void next_returnsAVerse() {
var rotator = newRotator(List.of(verse("A", "alpha")));
assertEquals("A", rotator.next().getReference());
}
@Test
void next_doesNotRepeatWithinCycle() {
var rotator = newRotator(List.of(verse("A", "a"), verse("B", "b")));
var first = rotator.next().getReference();
var second = rotator.next().getReference();
assertNotEquals(first, second);
}
@Test
void next_cyclesAfterAllShown() {
var rotator = newRotator(List.of(verse("A", "a"), verse("B", "b")));
rotator.next(); rotator.next();
// third call: should be one of A or B again (cycle reset)
assertNotNull(rotator.next());
}
@Test
void versesRemaining_decrementsWithEachNext() {
var rotator = newRotator(List.of(verse("A", "a"), verse("B", "b")));
assertEquals(2, rotator.versesRemaining());
rotator.next();
assertEquals(1, rotator.versesRemaining());
}
@Test
void resetCycle_restoresFullRemaining() {
var rotator = newRotator(List.of(verse("A", "a"), verse("B", "b")));
rotator.next();
rotator.resetCycle();
assertEquals(2, rotator.versesRemaining());
}
@Test
void next_persistsStateToFile() throws IOException {
Path stateFile = tempFile();
var rotator = newRotator(List.of(verse("A", "a")), stateFile);
rotator.next();
String contents = Files.readString(stateFile);
assertTrue(contents.contains("A"));
}
Six tests. They specify the public behavior completely. Helper methods (newRotator, verse, tempFile) hide setup noise.
That’s the test suite. Eight minutes. They all currently fail (no implementation yet). That’s correct.
Step 4 — Persistence shape (2 minutes)
The state file format. Simplest possible:
A
B
Plain text, one shown reference per line. Trivial to read with Files.readAllLines and to write with Files.write. JSON is overkill; pick simplicity.
If the file is missing, treat as empty (no verses shown yet). If the file is unreadable for some other reason, surface the exception. Both decisions go in a comment in the implementation.
Step 5 — Module-by-module, AI on (10 minutes)
Now we prompt. The first prompt:
“Write the
Verseclass. Two fields:String reference,String text. Constructor with both, public getters.equalsandhashCodebased onreferenceonly. No setters. Java 17. Output the class body only.”
The AI produces the class. You read it. It’s correct. You paste it. Move on.
Next prompt:
“Write the
VerseLoader.loadAll(Path csv)method that returnsList<Verse>. Each line in the CSV isreference,text. If the file is missing, throwIOException. If a line is malformed (no comma), throwIllegalArgumentExceptionwith the line number. Empty file returns an empty list. Use onlyjava.nio.fileandjava.io. Output only the method body.”
The AI produces. You read. Suppose it forgot to handle the line-number messaging properly — you redirect with one targeted prompt. Two prompts in, that method is done.
Next prompt:
“Write
DailyVerseRotator.next(). The class hasList<Verse> allVerses,Set<String> shownReferences,Path stateFile. The method should: pick any verse fromallVerseswhose reference is not inshownReferences, add that reference toshownReferences, persistshownReferencestostateFile(one reference per line), and return the verse. If all references are shown, clearshownReferencesfirst, persist the empty state, then pick. Output only the method body.”
The AI produces. You read. The persistence call uses Files.writeString instead of Files.write(Path, List<String>) — slightly less idiomatic but correct. You accept.
Keep going. Three more prompts; three more methods. The rotator is built.
Step 6 — Review (3 minutes)
Read every method the AI wrote. Specifically check:
- Any
==on Strings? (Coding 1 Ch 13 gotcha — Ch 7 accelerated; also Ch 11 review checklist.) - Any hallucinated APIs?
- Any null-handling decision that doesn’t match the spec?
- Any off-by-one risk in the verse-selection loop?
Find issues, redirect. Suppose resetCycle doesn’t persist the cleared state — fix that with one targeted prompt.
Step 7 — Integration test (1 minute)
Run the test suite. Six tests should pass. They do.
Run the application end-to-end with a sample CSV. The first verse prints. The state file appears. Run again — different verse. Run twice more — cycle exhausted, then resets.
Step 8 — Reflection (1 minute)
Write the reflection. Specifically:
What the AI got right first try:
- Verse class (clean, equals/hashCode by reference only).
- VerseLoader's basic structure.
- next() logic for picking an unseen verse.
What I redirected:
- VerseLoader line-number error messaging (took 1 redirect).
- resetCycle not persisting the cleared state (1 redirect).
- One subtler issue: AI used Files.writeString in one method
and Files.write(Path, List<String>) in another; I unified
these by hand for consistency.
What I wrote by hand because asking would have taken longer:
- All six JUnit tests.
- The architectural decision to use plain text instead of JSON for state.
- The choice of equals/hashCode on Verse based on reference only
(this was a spec decision, not an implementation one).
Reflection is specific. Done.
Total time: about 31 minutes.
For Project 14, you have 60 minutes for a slightly larger scope. The pace is feasible if you have done the reps and don’t panic.
Coach’s Note — Notice the time budget. Roughly 5 minutes for spec+architecture, 8 minutes for tests, 10 minutes for AI-assisted implementation, 3 minutes for review, 1 minute for integration, the rest is buffer. The pre-coding work — spec, architecture, tests — is a third of the total time. That is the right ratio. Students who burn 50 minutes on implementation and 0 on spec produce worse code in more total time.
15.4 — Skills That Reward the Most Practice This Week
Not every skill needs equal time. Here are the ones the final tends to expose, in order:
1. Writing a spec quickly (Ch 2)
The students who write good 3-minute specs ace the final. The students who skip this step and start coding spend the rest of the hour cleaning up architectural confusion. Practice 3-minute specs — see Drill 1 in the exercises.
2. Test-first discipline (Ch 4)
The final requires “at least 6 JUnit tests written before the code.” If you have to think hard about how to write a JUnit test, you will lose 10 minutes you cannot afford. Practice writing test suites from a spec until it is finger memory.
3. Scoping prompts (Ch 10, 13)
If your prompts are sloppy, you will waste minutes correcting AI output that you should have constrained better the first time. Drill 4 in the exercises is a timed prompting exercise; do it twice this week.
4. Reading AI output for bugs (Ch 11)
You will produce code with the AI. Some of it will be subtly wrong. Catch it before submitting. Practice scanning a 20-line AI-produced method in under 60 seconds for the Ch 11 failure modes.
5. Catching architectural drift (Ch 12)
The most common failure mode under exam pressure: starting with a clean three-class architecture and ending with everything stuffed into one class because you ran out of time. The skill is holding the line on architecture even when you’re rushed. Drill 3 in the exercises simulates this pressure.
6. The reflection (Ch 14)
The reflection is graded. It is also the easiest thing to skip with five minutes left. Budget 2 minutes for it. If you skip it entirely, you lose meaningful points even with otherwise-perfect code.
15.5 — Bugs Likely on the Final
Same list as Chapter 16’s Common Bugs (Coding 1), updated for Phase 2 conditions. Tape this to your wall.
==on Strings. Use.equals(). Still the most common single bug.ArrayList<int>. UseInteger.- NullPointerException. Some reference was null when you assumed it wasn’t.
- Missing
@Override. Silent failure of polymorphism. - Scanner
nextInt()thennextLine(). Empty line. Flush. - File not found, unhandled. Wrap I/O in try/catch or declare
throws. - Hallucinated API. AI invented
String.reverse()orLocalDate.fromString(). Compile and check. - AI rewrote the wrong method. Diff the file before accepting.
- Test passes, code is wrong. The test was weaker than the contract. Re-read the spec.
- You forgot to log a prompt. Log in real time, not at the end.
The new entries (7–10) are Phase 2 specific. The classic ones (1–6) still bite.
15.6 — The Study Plan
You have one week before the final. Two versions of the plan below. Pick the one that matches when you read this chapter.
Version A: 7 days (you read this on Sunday, exam is the following Sunday)
Day 1 (Sunday) — Read. Re-read this chapter. Re-read Chapter 8 (Coding 2) — the midterm-review chapter — for the collections refresher. Skim the worked example in §15.3 once more.
Day 2 (Monday) — Test discipline. Do Drill 1 (3-minute spec) and Drill 2 (test suite from spec) in the exercises. Each twice. AI off for both.
Day 3 (Tuesday) — Prompting and review. Do Drill 3 (architecture under pressure) and Drill 4 (prompting with constraints). AI on for both, but time yourself.
Day 4 (Wednesday) — Full mini-capstone, AI on. Do Drill 5 — full 45-minute mini-capstone. Use everything. Log every prompt. Reflect.
Day 5 (Thursday) — Review.
Re-read your prompts.txt from Projects 9–13. Pick out 3 prompts that worked well and 3 that didn’t. Write them in my-prompts.txt so they’re in front of you on exam day.
Day 6 (Friday) — Second full run. Do Drill 5 again with a different scope. Same time limit. Different problem. The variety is the rep.
Day 7 (Saturday) — Light review.
Re-read §15.5 (the bug list). Re-read §15.3 (the worked example). Re-read your own my-prompts.txt. Get a real meal. Sleep early.
Exam day — show up rested. Trust your training. Submit something honest.
Version B: 3 days (you read this on Thursday, exam is Sunday)
You’re behind. Don’t panic. The 3-day plan is real and works.
Day 1 (Thursday) — Read + one full drill. Re-read this chapter. Skim Chapter 13 (iterative refinement) and Chapter 12 (architecture). Then do Drill 5 — full 45-minute mini-capstone, AI on. Log every prompt.
Day 2 (Friday) — One more full drill plus the bug list. Do Drill 5 again, different scope. Compare to yesterday’s run. Then memorize §15.5’s bug list.
Day 3 (Saturday) — Light review. Re-read your two drill runs. Identify the one skill that gave you the most trouble. Re-read the chapter for that skill. Then rest. Real meal. Sleep.
Exam day — same advice. Trust the (compressed) training.
Coach’s Note — Three days of focused practice beats seven days of unfocused practice. The students who do 2 full timed drills the week before usually outperform the students who do 6 hours of “review” without ever simulating the actual conditions. The exam is a 60-minute time-pressured build; the most useful preparation is 60-minute time-pressured builds.
15.7 — Exam Day Logistics
Same as Coding 1’s final, with the Phase 2 differences:
- 60 minutes (your instructor may adjust to 75).
- Open-textbook. This textbook (printed or non-interactive PDF) is allowed.
- Open-AI. The course’s default AI assistant is allowed, exactly as you’ve used it in Phase 2 projects.
- Closed-internet. No browsing other than what the AI assistant interface itself requires.
- No human help. No DMs, no Discord, no asking a friend. The exam is solo.
- Submit one URL — your OnlineGDB Java project link (recommended) or a GitHub repo URL. See Project 14 for the exact submission requirements.
The exam project is P14: A Real Small Application. The full spec is in Project 14. Read it now if you haven’t. Read it again Saturday night. The list of approved scopes is in the spec.
Required deliverables for the final: a spec doc, 6+ JUnit tests written before code, persistent storage, exception handling, a documented module architecture, at least 2 AI-built methods with prompt log, and a reflection.docx. All seven are graded.
15.8 — A Direct Word on the AI Question (One More Time)
The final is open-AI by design. That is not because AI makes the exam easier. It is because, after fifteen weeks of practice, AI is part of your working environment, and pretending it isn’t would falsify the assessment.
The trap of an open-AI exam is the opposite of the trap of a closed-AI exam. Closed-AI exposes whether you have the skills the AI normally covers for. Open-AI exposes whether you have the senior skills the AI cannot cover for. Both exposures are honest. Both are graded honestly.
The student who does well on the open-AI final:
- Writes a spec quickly and clearly.
- Writes tests before prompting the AI.
- Asks the AI for one method at a time, scoped, with constraints.
- Reviews the AI’s output for the bugs from Ch 11.
- Logs every prompt as they go, not at the end.
- Reflects honestly at the end on what the AI did and what they did.
The student who does badly on the open-AI final:
- Skips the spec, starts prompting cold.
- Asks the AI for “a small application” in one prompt and accepts what comes back.
- Doesn’t write tests.
- Doesn’t review the output.
- Forgets to log prompts and writes them from memory at the end (which is dishonest if done in a way that misrepresents what they sent).
- Submits a polished-looking program they don’t fully understand.
The first student’s transcript and the second student’s transcript may both show the same letter grade for Phase 1. The final is going to separate them anyway. That separation is the point.
Coach’s Note — I have never seen, in any iteration of this course, a student who did badly on the final after consistently doing the reps. I have seen, more than once, students who did badly on the final after coasting through Phase 2 by accepting AI’s first answers without engaging. The final exposes the difference. There is no way to fake it. Show up with the skill or show up without it; the test is the test.
15.9 — Coach’s Final Word for Week 15
This is the last chapter before the final. Next week’s chapter is just the final-review wrap-up around Project 14; it adds essentially no new material.
You have, by now, written more Java than most working programmers had written by month 6 of their first job. You have written specs. You have written tests before code. You have directed an AI partner through five projects of growing scope. You have learned, in your own hands, what the senior engineer’s job actually is.
Spend this week consolidating. Do the drills. Do them timed. Do them honestly. Sleep enough.
The capstone is one week away. It is built to be passable by every student who did the reps. It is built to be illuminating to every student who didn’t. Show up rested. Trust your training. Submit something honest.
See you Monday.
Up next: Read the exercises — Capstone Drills. Do them in order. Then read Chapter 16 for the wrap-up and Project 14 for the capstone spec.