Choosing Your Next Python Path
Seven exits off the same road, and how to pick by what you want to build
In this lesson
You have finished a curriculum. From here there is no next lesson to click, which is its own kind of difficulty: the problem stops being “what do I learn?” and becomes “what do I choose?”
This lesson lays out seven honest directions, says what each one is really like day to day, and gives you a way to pick that does not depend on guessing which one is most employable.
Explain it like I’m 5
Python is one road with several exits. You do not need to drive all of them. Pick the exit that leads to something you actually want to exist.
Pick the exit, not the road
The advice you will hear most is to choose the path with the best job market. It is bad advice for one practical reason: you will not finish a project you do not want, and the unfinished project teaches you nothing.
So invert it. Start from a thing you want to exist — a page your team can post to, a chart of your own spending, a script that renames four hundred photos — and let that choose the path. Motivation is the scarce resource here, not information.
Everything below is described in terms of what you would build, because that is the only part of a path you can judge before you are in it.
Every one of these doors is already unlocked
Before the descriptions, the useful fact: you are not at the start of any of these paths. Each one builds on units you have already done.
FINISHED = set(range(1, 16)) # units 1-15, which you have just done
NEEDS = {
"web": {2, 3, 6, 8},
"data": {2, 3, 12},
"automation": {3, 5, 9},
"backend": {7, 8, 9},
"security": {7, 8},
"games": {14},
"core Python": {10, 11},
}
for path, needed in NEEDS.items():
missing = sorted(needed - FINISHED)
print(f"{path:12s} builds on units {sorted(needed)} missing: {missing or 'none'}")
web builds on units [2, 3, 6, 8] missing: none data builds on units [2, 3, 12] missing: none automation builds on units [3, 5, 9] missing: none backend builds on units [7, 8, 9] missing: none security builds on units [7, 8] missing: none games builds on units [14] missing: none core Python builds on units [10, 11] missing: none
The seven paths, honestly described
Each entry says what you build, what to learn first, and the part people leave out.
- Web development. Build: sites with accounts, forms, and a database. Next: Flask you already have, so learn Django for the batteries-included framework, or FastAPI if you mostly serve data. The part people leave out: half of web work is not Python. Expect HTML, CSS, forms, sessions, and deployment.
- Data analysis. Build: answers, charts, and reports from real messy files. Next: more pandas, then Jupyter notebooks, then a dashboard tool such as Streamlit. The part people leave out: most of the job is cleaning, not modeling — Unit 12 was not exaggerating.
- Automation and operations. Build: scripts that do a chore on a schedule, on your machine or a server. Next: cron or Task Scheduler, then a cloud provider's basics, then containers. The part people leave out: the code is the easy half; making it fail safely and tell you about it is the rest, which is why logging exists.
- Backend and APIs. Build: services other programs call, with a database behind them. Next: FastAPI, then Postgres and real SQL, then authentication. The part people leave out: you will spend real time on other people's API quirks, not just your own design.
- Security scripting. Build: tools that check your own systems — audit a configuration, hash and compare files, scan your own site's headers. Next: requests in anger, then
hashlibandsecrets, then a deliberate reading of the OWASP top ten. The part people leave out: permission is the whole subject. Testing systems you do not own or have written authorization for is a crime in most countries, regardless of intent. - Games and interfaces. Build: small games and desktop tools. Next: more Pygame, or Qt for serious desktop apps. The part people leave out: art and sound take longer than the code, and Python is a rare choice for commercial games.
- The language itself. Build: libraries and tools other developers use. Next: the next lesson, then reading the standard library's own source. The part people leave out: this one is best taken second. It sharpens work you are already doing; on its own it has no project to point at.
The decision table
Read this as “I want to build X, so I learn Y next”. It is deliberately blunt.
- A page other people can log into → web
- A chart or a number I currently work out by hand → data
- Something that runs every night without me → automation
- Something another program talks to → backend
- A check on my own systems → security
- Something a person plays with → games
- Code that is faster, or a library others install → core Python
If your goal fits two rows, it is two projects. Split it and build the smaller one.
Fill in the table for yourself. Replace each "?" with one of the seven path names, then run it: unanswered() reports any goal that still has no path, or a path spelled in a way that is not on the list.
PATHS = ("web", "data", "automation", "backend", "security", "games")
GOALS = {
"a page my friends can post to": "?",
"find out which month I spend most": "?",
"rename 400 photos by the date they were taken": "?",
"let another program read my data": "?",
"check my own site for weak settings": "?",
"something my nephew can play": "?",
}
def unanswered(goals):
"""Goals with no path yet, or a path that is not on the list."""
return [] # TODO: keep any goal whose path is not in PATHS
for goal, path in GOALS.items():
print(f"{path:11s} {goal}")
print("unanswered:", unanswered(GOALS))
print("paths used:", len(set(GOALS.values())), "of", len(PATHS))
unanswered is one comprehension over goals.items(), keeping the goal whenever path not in PATHS — which catches both a leftover "?" and a typo such as "webdev". Wrap it in sorted() so two runs agree.
PATHS = ("web", "data", "automation", "backend", "security", "games")
GOALS = {
"a page my friends can post to": "web",
"find out which month I spend most": "data",
"rename 400 photos by the date they were taken": "automation",
"let another program read my data": "backend",
"check my own site for weak settings": "security",
"something my nephew can play": "games",
}
def unanswered(goals):
"""Goals with no path yet, or a path that is not on the list."""
return sorted(goal for goal, path in goals.items() if path not in PATHS)
for goal, path in GOALS.items():
print(f"{path:11s} {goal}")
print("unanswered:", unanswered(GOALS))
print("paths used:", len(set(GOALS.values())), "of", len(PATHS))
web a page my friends can post to
data find out which month I spend most
automation rename 400 photos by the date they were taken
backend let another program read my data
security check my own site for weak settings
games something my nephew can play
unanswered: []
paths used: 6 of 6
One path at a time, and here is the arithmetic
The most common way to lose the next six months is to start three paths at once. It feels efficient. It is the opposite, and you can work out why on paper.
# Very roughly, 40 hours is where a new path starts being useful on its own.
ATTEMPTS = [("one path", 1), ("two paths", 2), ("three paths", 3)]
def weeks_to_useful(paths_at_once, hours_per_week, threshold=40):
"""Weeks until the FIRST path reaches the useful threshold."""
hours_each = hours_per_week / paths_at_once
return threshold / hours_each
for label, paths in ATTEMPTS:
weeks = weeks_to_useful(paths, 6)
print(f"{label:11s} at 6 h/week: {weeks:.0f} weeks before anything is useful")
one path at 6 h/week: 7 weeks before anything is useful two paths at 6 h/week: 13 weeks before anything is useful three paths at 6 h/week: 20 weeks before anything is useful
Test-drive a path in one weekend
You cannot tell from a description whether you will enjoy something. You can tell in two hours. So before committing a season to a path, spend one afternoon on its smallest real version:
- Web: add one new page and one form to the Flask notes app.
- Data: load your own bank export or step counts into pandas and chart one month.
- Automation: write the script that tidies your actual downloads folder, in
--dry-runmode only. - Backend: serve one JSON endpoint that returns something real, following Unit 7.
- Security: write a script that reports the response headers your own site sends.
- Games: make one square move with the arrow keys and stop at the edges.
- Core Python: take your slowest script and try profiling it before changing a line.
Afterwards, ask one question: did the next two hours sound like a chore or like a plan? That answer is worth more than any comparison of job markets, because it is about you rather than about the average of everyone.
Common mistake: Picking the path that sounds most impressive
One of these is always in the headlines, and it is easy to read that as “the correct answer”.
Ask what you would build in week two. If you cannot name something you want to exist, that path will not survive contact with a wet Tuesday. Pick the one with a project you would use.
Common mistake: Starting three paths at once
They all look interesting, and choosing feels like giving something up.
Do the arithmetic above. One path for six weeks, then reassess. Nothing is closed off by starting somewhere.
Common mistake: Doing another course instead of the next project
A course tells you exactly what to do next, and being told is comfortable. A blank folder is not.
Notice that a course you have already half-covered feels productive precisely because it is easy. Build the smallest thing on your chosen path first, and take the next course when you hit something you genuinely cannot do.
Common mistake: Abandoning a path because week one was frustrating
Week one of anything is setup, error messages, and no visible progress, which is easily mistaken for a bad fit.
Judge a path on the day you first build something that works, not on the day you fight its installation. Give it the two-hour test-drive above, and then a fortnight, before deciding.
Common mistake: Treating “security” as license to poke at other people's systems
The tutorials look the same whether the target is yours or not.
Point every tool you write at systems you own or have written permission to test, and use a deliberately vulnerable practice target otherwise. The technical skill is legal; using it uninvited is not.
You want a site your five-a-side team can log into and post fixtures. Which path?
Logging in and posting means accounts, forms, and a database behind pages, which is web development. You already have the Flask half of it from Unit 6.
Every month you clean and summarize 300 spreadsheets by hand. Which path?
Cleaning and summarizing tabular data is pandas work — Unit 12 — and once the script exists, a scheduler is a small addition rather than a separate project.
Why choose a path by the project rather than by the job market?
Motivation is the limiting factor, not information. An unfinished project on the fashionable path is worth less than a finished one on any path.
You genuinely want to try two paths. What is the better plan?
Splitting your hours pushes the first useful moment out by months. Sequential costs you nothing in the end and gets you a working project far sooner.
Mini exercise (medium)
Turn the decision table into code that reads a goal written in ordinary words. paths_for(goal) returns every path whose keywords appear in the goal, sorted. path_for(goal) then gives the single answer — or "too vague" when nothing matches and "too broad" when several do, because a goal that spans two paths is two projects.
Take the wheel. Complete the code, hit Run, and check your output right here.
KEYWORDS = {
"web": ("website", "page", "log in", "browser"),
"data": ("chart", "spreadsheet", "trend", "csv"),
"automation": ("rename", "folder", "every night", "backup"),
"backend": ("api", "endpoint", "another program"),
"security": ("permission", "hash", "audit"),
"games": ("game", "play", "sprite"),
}
WISHES = [
"a website my band can log in to",
"a chart of my spending trend",
"rename every folder of photos by date",
"an api another program can call",
"a game with sprites on a web page",
"something to help me at work",
]
def paths_for(goal):
"""Every path whose keywords appear in the goal, sorted."""
return [] # TODO
def path_for(goal):
"""The single path a goal points at, or why it does not point at one."""
return "?" # TODO: exactly one match -> that path
# TODO: none -> "too vague"; several -> "too broad"
for goal in WISHES:
print(f"{path_for(goal):11s} {goal}")
paths_for is one comprehension over KEYWORDS.items(), keeping a path when any(word in goal.lower() for word in words). Lower-case the goal once before the loop rather than inside it. Then path_for only has to look at len(matches).
KEYWORDS = {
"web": ("website", "page", "log in", "browser"),
"data": ("chart", "spreadsheet", "trend", "csv"),
"automation": ("rename", "folder", "every night", "backup"),
"backend": ("api", "endpoint", "another program"),
"security": ("permission", "hash", "audit"),
"games": ("game", "play", "sprite"),
}
WISHES = [
"a website my band can log in to",
"a chart of my spending trend",
"rename every folder of photos by date",
"an api another program can call",
"a game with sprites on a web page",
"something to help me at work",
]
def paths_for(goal):
"""Every path whose keywords appear in the goal, sorted."""
lowered = goal.lower()
return sorted(path for path, words in KEYWORDS.items()
if any(word in lowered for word in words))
def path_for(goal):
"""The single path a goal points at, or why it does not point at one."""
matches = paths_for(goal)
if len(matches) == 1:
return matches[0]
return "too vague" if not matches else "too broad"
for goal in WISHES:
print(f"{path_for(goal):11s} {goal}")
web a website my band can log in to
data a chart of my spending trend
automation rename every folder of photos by date
backend an api another program can call
too broad a game with sprites on a web page
too vague something to help me at work
assert paths_for("a chart of my spending trend") == ["data"], "chart and trend are both data keywords, so the answer is one path"
assert paths_for("something to help me at work") == [], "no keyword matches, so no path matches"
assert paths_for("a game with sprites on a web page") == ["games", "web"], "return every match, sorted - this goal really does span two paths"
assert path_for("an api another program can call") == "backend", "one match means that is the path"
assert path_for("something to help me at work") == "too vague", "nothing matched: the goal is not specific enough to choose from"
assert path_for("a game with sprites on a web page") == "too broad", "two matches means the goal is two projects"
print("\u2713 Looks good!")