Capstone: Planning & Architecture
What You'll Build Today
Welcome to Day 76. You have spent the last 75 days learning Python, APIs, databases, vector stores, and prompt engineering. You are now a builder.
Today, you start your Capstone Project. This is the centerpiece of your portfolio. It is the proof that you can not only write code but also architect a solution to a real-world problem.
We are not writing the application logic today. Instead, we are building the Blueprint.
You will learn:
* Problem Scoping: Why you must define exactly what you are solving before you type import.
* MVP (Minimum Viable Product) Strategy: Why building fewer features makes for a better project.
* System Architecture: How to draw the map of your data flow so you don't get lost.
* Tech Stack Selection: How to choose the right tools (and justify those choices).
This is the difference between a "tutorial follower" and a "software engineer." Let's get to work.
---
The Problem
You might be tempted to skip this day. You might think, "I know what I want to build, I'll just start coding."
This is the "Spaghetti Trap."
When you start coding without a plan, your code usually ends up looking like a single, massive file where logic, user interface, and database connections are hopelessly tangled.
Here is what it looks like when you don't plan your architecture. This is a snippet from a hypothetical project called main.py where a student tried to build a "Chat with PDF" app on the fly.
The "Spaghetti Code" Nightmare
# spaghetti_bot.py
# The result of "coding before planning"
import streamlit as st
import openai
import sqlite3
import os
# PROBLEM 1: Hardcoding configurations everywhere
openai.api_key = "sk-..."
st.title("My PDF Chat")
# PROBLEM 2: Database logic mixed with UI code
conn = sqlite3.connect('my_db.db')
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS chats (msg text)''')
uploaded_file = st.file_uploader("Upload PDF")
if uploaded_file:
# PROBLEM 3: Heavy processing logic blocking the UI thread
# Imagine 100 lines of PDF parsing code right here...
text = "extracted text..."
user_input = st.text_input("Ask a question")
if user_input:
# PROBLEM 4: Direct API calls mixed with presentation logic
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": user_input + text}]
)
# PROBLEM 5: No separation of concerns.
# If you want to change the database later, you have to rewrite the UI.
c.execute(f"INSERT INTO chats VALUES ('{user_input}')")
conn.commit()
st.write(response.choices[0].message.content)
Why this hurts
There is a better way. We separate the Plan from the Code.
---
Let's Build It
We are going to define a Capstone Project together. For this tutorial, we will plan a project called "JobHunter AI"βan assistant that analyzes your resume against a job description and suggests improvements.
You will follow this process for your own unique idea in the Challenge section.
Step 1: The Problem Statement & User Persona
Before we choose a database, we must define the "Who" and the "Why."
The Problem: Job seekers apply to dozens of jobs but get rejected by Applicant Tracking Systems (ATS) because their resumes don't match specific keywords in the job description. Customizing resumes manually takes too long. The User: "Alex," a mid-level marketing manager looking for a new role. Alex is non-technical, stressed, and needs quick, actionable advice, not complex charts. Why this step matters:If we didn't define Alex as "non-technical," we might have built a complex dashboard with JSON outputs. Since Alex is non-technical, we know we need a simple Chat Interface.
Step 2: The MVP (Minimum Viable Product)
We cannot build everything in one week. We must ruthlessly cut features. We will categorize features into "Must Have" (MVP) and "Nice to Have" (V2).
Must Have (MVP):By cutting "PDF Upload" and "User Accounts," we just saved ourselves roughly 15 hours of coding. We can add them later if the core AI logic works.
Step 3: System Architecture
Now we map how data moves. We will not write the app code yet, but we will write a Python script that generates our project structure based on a clean architecture.
We will use a standard pattern:
Let's write a script to set up this architecture physically.
import os
def create_project_structure(project_name):
"""
Creates a clean, architectural folder structure for the Capstone.
"""
base_path = f"./{project_name}"
# Define the folder structure
structure = {
"app": ["__init__.py", "main.py", "ui_components.py"],
"core": ["__init__.py", "resume_analyzer.py", "llm_interface.py"],
"data": ["__init__.py", "sample_resume.txt", "sample_job_desc.txt"],
"utils": ["__init__.py", "config.py", "prompts.py"],
".": ["requirements.txt", "README.md", ".env"]
}
print(f"ποΈ Scaffolding project: {project_name}...")
# Create directories and files
for folder, files in structure.items():
# Create folder path
if folder == ".":
folder_path = base_path
else:
folder_path = os.path.join(base_path, folder)
os.makedirs(folder_path, exist_ok=True)
# Create files inside
for file in files:
file_path = os.path.join(folder_path, file)
with open(file_path, 'w') as f:
# Add a docstring to explain the file's purpose
if file.endswith(".py"):
f.write(f'"""\nModule: {file}\nPurpose: [Write purpose here]\n"""\n\n')
elif file == "README.md":
f.write(f"# {project_name}\n\n## Problem Statement\n\n## MVP Features\n")
elif file == ".env":
f.write("OPENAI_API_KEY=sk-...\n")
print(f" Created: {file_path}")
print("\nβ
Project structure ready. No spaghetti code allowed!")
# Run the scaffolder
create_project_structure("JobHunter_AI")
Run this code. You will see a new folder JobHunter_AI appear.
Why this step matters:
We have physically separated the ui (Streamlit) from the core (Logic).
* app/main.py will handle buttons and text inputs.
* core/resume_analyzer.py will handle the thinking.
* utils/prompts.py will store our prompt templates so they don't clutter the code.
Step 4: Tech Stack Rationale
You must justify your tools. Here is the rationale for JobHunter AI:
Step 5: Defining the Interface (Pseudo-code)
Before we write the real backend tomorrow, let's sketch the app/main.py to see if our architecture makes sense.
We will write this into JobHunter_AI/app/main.py.
"""
Module: main.py
Purpose: The entry point for the Streamlit application.
ONLY handles UI logic. No business logic here.
"""
import streamlit as st
# Notice we import from our 'core' module (even though we haven't written it yet)
# from core.resume_analyzer import analyze_match
def render_header():
st.header("JobHunter AI π―")
st.markdown("Optimize your resume for any job description.")
def render_inputs():
col1, col2 = st.columns(2)
with col1:
resume = st.text_area("Paste Resume Text", height=300)
with col2:
job_desc = st.text_area("Paste Job Description", height=300)
return resume, job_desc
def main():
render_header()
resume, job_desc = render_inputs()
if st.button("Analyze Match"):
if not resume or not job_desc:
st.warning("Please provide both texts.")
return
with st.spinner("Analyzing keywords..."):
# This function doesn't exist yet, but we know we NEED it.
# result = analyze_match(resume, job_desc)
# Placeholder for today
st.success("Analysis Complete (Mock)")
st.json({
"score": 85,
"missing_keywords": ["Python", "SQL"],
"advice": "Add more metrics to your experience."
})
if __name__ == "__main__":
main()
Why this step matters:
This is "Interface-Driven Development." We defined how the app looks and what data it needs from the backend. Now, we have a clear specification for tomorrow: we need to write a function analyze_match that takes two strings and returns a JSON object.
---
Now You Try
You have the structure for "JobHunter AI." Now, practice adapting the plan.
Change the user from "Job Seeker" to "HR Recruiter."
Task:* Update the MVP feature list. Does a recruiter paste one resume? Or do they need to upload 50 resumes and rank them? (Hint: This changes the architecture significantly).Imagine the resume data is highly confidential and cannot be sent to OpenAI.
Task:* Update the Tech Stack Rationale to use a local model (like Ollama/Llama3) instead of GPT-4.database.py file to your folder structure script to accommodate this.
---
Challenge Project: Your Capstone Plan
It is time. You are going to plan your project.
The Goal: Create a comprehensivePLAN.md file for the project you will build over the next few days.
Requirements:
PLAN.md format:
``markdown
Problem
Home cooks often have random ingredients in the fridge but don't know what to make, leading to food waste and ordering takeout.
User
Busy parent who wants quick recipes based on available inventory.
MVP Features
Architecture
User Input -> Prompt Template -> OpenAI GPT-4 -> JSON Parser -> Streamlit Display
Tech Stack
- Streamlit: UI
- LangChain: Prompt management
- OpenAI: Recipe generation
Guidance:
* Keep it simple. A finished simple project is infinitely better than a half-finished complex one.
* Do not start coding the logic today. Just set up the folder structure and write the
PLAN.md`.
* Show this plan to a friend (or rubber duck) and explain it. If you can't explain the flow, you can't code it.
---
What You Learned
Today you learned that coding is the last step of software engineering.
* Problem Scoping: You defined boundaries to prevent "feature creep."
* Separation of Concerns: You learned to separate UI code from Logic code using a directory structure.
* MVP Thinking: You learned to focus on the "Must Haves" to ensure you actually ship a product.
* Interface Design: You sketched the UI to define what the backend needs to do.
Why This Matters:In a real job interview, they will ask, "Tell me about a time you designed a system." They don't want to hear about syntax. They want to hear about trade-offs, user needs, and architecture. This plan gives you that story.
Tomorrow: We take our plan and breathe life into it. We will build the Backend Logic for your Capstone.