Engineering first, interviews next, ML later
Part 1 prepares you to build, debug, test and explain backend software, then demonstrate those skills in software-engineering interviews. Part 2 is a separate ML curriculum for after the readiness check.
Keep your current job10 hrs/week maximumOne backend project~25 weeks / ~250 hours initially
| Stage | Starting estimate | Outcome |
|---|---|---|
| E — Engineering | 13 weeks / 130 hours | A working backend with evidence of correctness, debugging, performance and recovery, then a first AI demo |
| I — Interview practice | 12 weeks / 120 hours | Unaided coding, practical debugging, SQL, system design and clear explanations of your work |
| Readiness check | Mocks from I8, recorded in I12 | Evidence reviewed by another engineer; repeat weak areas before starting Part 2 |
Target: general software/backend interviews at your experience level. In E1, spend 90 minutes on 20 relevant local or eligible remote postings. For each, note the backend language and framework, whether the role is backend-only or full-stack, any cloud provider it names (AWS, GCP, Azure), the interview format, whether it asks for experience with AI coding tools or LLM features, and the education line — including whether it says "or equivalent experience". Save the file: E1 uses it to confirm the language, and E9 to choose where the shop runs. Use that evidence to adjust Stage I; add a topic only by replacing another within the cap. You do not need to start applying or get an offer before ML.
First study session: spend 60 minutes tracing one endpoint in an existing app: explain a database rule, the result of a repeated request, the cost of one loop and the test you would use to catch a failure. Record what you can explain and what needs practice. This session and the posting scan together take 2½ of E1's ten hours; unavailable access means untested. If the app is your employer's, keep its code out of AI tools — see the data boundary.
Week tracker
Tick a week when it is done, or skipped on purpose. Ticks are saved in this browser only; the sidebar shows your progress and links to the first unticked week.
Stage E — Engineering
- E1 Git and the local stack
- E2 Data model and migrations
- E3 Testing, plus the shell
- E4 HTTP and API design · pace check · name a reviewer
- E5 Auth and security
- E6 The browser side
- E7 Transactions and concurrency
- E8 Performance
- E9 Background jobs · choose the host · pace check
- E10 External calls and caching
- E11 Observability and debugging
- E12 CI/CD, deploy, peer review
- E13 First AI demo, tagged v0
Stage I — Interview practice
I1–I3 · arrays, strings, two pointers, sliding windows
I4–I6 · stacks, queues, linked lists, binary search, trees
I7–I9 · heaps, graphs, backtracking, 1-D DP
I10–I12 · mixed unseen problems
Stage E — Software engineering fundamentals
Duration: Weeks E1–E13 — about 130 hours at ten a week; extend when a layer needs more practice
Build and operate one backend application, one layer at a time, then ship a first AI demo in E13. The goal is independent implementation, debugging and explanation. Interview practice follows in Stage I; ML starts only after the Part 1 readiness check.
Why one app, built layer by layer
These skills support your current engineering work: data correctness, tests, API behavior, security, concurrency and diagnosing failures. One app built layer by layer makes you maintain earlier decisions as requirements grow. The app is practice; sustained work on real systems continues to develop these skills after this plan.
The database layer is the SQLAlchemy 2.0 ORM, used synchronously — not async. Alembic runs on SQLAlchemy, and async sessions raise an error on a lazy load instead of quietly running one query per row, which would hide the N+1 you fix in E8. FastAPI's tutorial uses SQLModel for its database pages; skip those and use SQLAlchemy's Unified Tutorial instead, one part at a time in E2, E4 and E8.
| Week | Topic | Build — done when | Source |
|---|---|---|---|
| Git: branches, rebase, bisect, revert. Docker Compose for the local stack | FastAPI skeleton and Postgres in Docker Compose, with a health endpoint that queries the database — starts with one command from a fresh clone | missing.csail.mit.edu — the Git lecture; docs.docker.com — Compose quickstart | |
| Data modelling: keys, normalization, constraints, migrations — as SQLAlchemy models and Alembic migrations | Users, products, orders and order items as SQLAlchemy models and Alembic migrations, with a constraint for every rule the database can enforce — stock never negative, email unique, no orphaned order items | postgresql.org — Data Definition; docs.sqlalchemy.org — Unified Tutorial: Working with Database Metadata; alembic.sqlalchemy.org — Tutorial | |
| Testing: unit vs integration, fixtures, test databases, what not to mock. The weekday reading also covers the shell: processes, pipes, environment variables, permissions | Tests against a real Postgres, not mocks, run with one command — including one that fails if an E2 constraint is dropped | docs.pytest.org — getting started and fixtures; missing.csail.mit.edu — the shell lectures | |
| HTTP and API design: methods, status codes, validation, error format, pagination, idempotency keys | Product and order endpoints with validation, one error format, cursor pagination, and an idempotency key on order creation — tested by sending the same request twice | developer.mozilla.org — HTTP; docs.sqlalchemy.org — Unified Tutorial: Working with Data, and Data Manipulation with the ORM | |
| Auth and security: password hashing, sessions vs tokens, authorization, the OWASP Top 10, secrets | Sign-up, login, and orders scoped to their owner. The test that matters: user A asking for user B's order gets a 404 | owasp.org — Top 10 | |
The browser side: HTML, CSS, JavaScript, fetch, cookies, CORS, CSRF | One plain HTML page, no framework, that logs in, lists products and places an order. Break CORS on purpose once and read the browser's error | developer.mozilla.org — Using Fetch | |
| Transactions and concurrency: isolation levels, lost updates, row locks, optimistic locking; async vs threads in Python | Write the naive version first: read the stock, check it in Python, write the new value back — product.stock -= qty in the ORM does exactly this. An atomic stock = stock - 1 would let E2's stock >= 0 rule hide the race. A test fires 20 concurrent orders at the last 5 items, each on its own session, with a delay patched in between the read and the write so they overlap on every run. It asserts that at most 5 orders succeed and that the final stock is 5 minus the orders that did — and it fails on the naive version. Fix it twice — a row lock, then a conditional update — and keep the test | postgresql.org — Concurrency Control | |
Performance: indexes, EXPLAIN ANALYZE, N+1 queries, the cost of your own loops | Seed a million rows. Fix one N+1, one missing index, and one list scan that should be a dict or set. Before/after timings in the README | use-the-index-luke.com; docs.sqlalchemy.org — Unified Tutorial: Working with ORM Related Objects (loader strategies) | |
| Redis for background jobs: queues, at-least-once delivery, retries with backoff, idempotent consumers, dead letters; the transactional outbox | Order confirmations sent by an RQ worker with retries, passing the order ID as an idempotency key to a fake email sender that ignores repeats. The order and an outbox row commit in one transaction, and a relay queues unsent outbox rows. Tests: the same job delivered twice, a worker killed after sending but before recording success, and the API stopped right after the commit each end with one email; an order that rolls back ends with none. The queue only gives at-least-once; the outbox makes sure a job exists, and the key makes it one email. Also choose and check the E12 host — see below | python-rq.org; microservices.io — Transactional outbox | |
| Networking and external calls: DNS, TCP, TLS, what happens when you type a URL; timeouts, and what a retry can repeat. Cache-aside and invalidation | Call one external API (exchange rates, say) with a timeout and bounded retries, with tests for the slow and failing paths. Inspect the call with curl -v, dig and openssl s_client. Cache one read path from E8 in Redis with a written invalidation rule, and time it | hpbn.co — the networking chapters | |
| Observability and debugging: structured logs, request IDs, metrics, traces, profiling, the debugger | A request ID in every log line; request rate, errors and latency exposed; one slow path found with a profiler and fixed. Inject one bug and write a short incident note: symptom, how you found it, fix | sre.google — Monitoring Distributed Systems | |
| CI/CD and running it: pipelines, migrations on deploy, secrets, backups | GitHub Actions runs the tests against Postgres on every push and deploys on merge to the host chosen in E9; the app is live within the spending cap; a backup restored into a separate database and checked | docs.github.com — Actions | |
| A first AI demo: an LLM API, embeddings and naive retrieval — no ML theory needed | A command-line tool over a folder of documents: chunk them naively, embed them, store them in the simplest vector store you can find, and answer questions with an LLM. It prints the passages it retrieved with each answer and reads model names from environment variables. No auth, database, UI, tests or Docker. Pushed to its own public repository with a README and an honest known-limitations section, then tagged v0 and never edited again | Your chosen LLM provider's documentation |
OptionalAlready know a week's topic?
Build its layer first, in under two hours. Official documentation is allowed; tutorials, AI and copied code are not. If its checks pass and you can explain every part of it, commit it and start the next week early — evidence, rather than familiarity with the topic name, is what lets you shorten the stage..env to .gitignore, and confirm git status is clean of the key before the first push. Every chunk you embed goes to a third-party API, so choose documents inside the data boundary: public documentation, an open dataset or your own writing. Use the weekdays to read the provider documentation, so the weekend is pure building.NoteWhy an AI demo now, and why v0 is frozen.
Application engineering — calling an LLM API and wiring up retrieval — needs no ML theory, and E10's timeouts and retries apply directly to LLM calls. Building it now gives you something concrete to talk about in Stage I, a real reason to care about Part 2's evaluation and retrieval material, and proof that the transition is achievable, months earlier than Part 2 would. It will be mediocre; that is the point. Part 2's Weeks 30 and 33 teach you why, and Week 30 starts Project #3 from a copy of this repository, so every improvement happens there. Printing the retrieved passages and reading model names from environment variables is what lets Part 2 Week 33 still score v0 after its models are retired — neither can be added once the tag is frozen. Shown side by side in Part 2 Week 40, the naive first attempt and the measured rebuild tell a story about your growth that neither tells alone.Stage I — Software-engineering interview practice
Duration: Weeks I1–I12 after Stage E — about 120 hours. Run this block before ML whether or not you have chosen an application date. The purpose is to demonstrate your engineering skills before moving to ML.
Practise problem-solving, design, runnable code, testing and explanations of previous work. These are covered in Microsoft's technical interview guidance; each employer's format still needs checking. No problem count guarantees a pass.
Coding and data structures — 4 hours each week
Use NeetCode's free problem list and LeetCode. Python is the default because Stage E has given you practice in it. If an upcoming interview requires another language, use that language in these same hours.
| Weeks | Coding practice | Engineering and design practice |
|---|---|---|
| I1–I3 | Big O; arrays, strings, dicts/sets; sorting, two pointers, sliding windows | Trace the shop's requests; SQL joins and aggregates without double-counting; data constraints; basic design of the shop API |
| I4–I6 | Stacks/queues, linked lists, binary search, recursion and trees | Indexes and query plans, transactions and locks, HTTP/TLS, processes vs threads vs async; design a URL shortener |
| I7–I9 | Heaps, graph BFS/DFS, backtracking and introductory 1-D dynamic programming | Retries, idempotency, caching, replication and consistency; design a rate limiter and notification service |
| I10–I12 | Mixed unseen problems, timed mocks and re-solving weak patterns; no new topic quota | Unfamiliar-code debugging, project explanation, mock feedback and the readiness check |
Two two-hour sessions per week. In I1–I6, attempt two easy problems per session, 20 minutes each — or one medium for 35–45 minutes once you can explain the pattern behind it. From I7, attempt one new problem per session for 35–45 minutes. No AI or solution open during an attempt. Clarify examples, explain a simple approach, implement, test and state time/space complexity. Use the remaining time for the editorial, a corrected solution and a re-solve of an earlier problem. Log assistance honestly in dsa/solved.md. Completion of NeetCode 150 is not required.
Fundamentals and system design — 2 hours each week
Use one hour to revisit a Stage E concept using its existing source and your own code. Use the other for a small design: requirements → rough scale → API → data model → components → one failure case. Consult the concept sections of the System Design Primer when needed. Begin with one service and one database; justify each added component. Save the diagram and unanswered questions in system-design/.
Start with the shop, then practise the shortener, rate limiter and notification service from the table. Defend constraints, consistency, retries and trade-offs before adding scale. Design alone first; another engineer or AI acting only as interviewer can ask follow-ups afterward.
Practical work and mocks — 3 hours each week
I1–I4: make one bounded change to the shop, reproduce a bug or review a small diff. Clarify the requirement, find the relevant code, make the smallest fix, add a meaningful regression test and explain the result. Request another engineer's review where possible and address it. In I2 and I4, make that week's shop change with an AI agent instead — see professional mode.
I5–I12: replace part of that slot with one 45–60 minute mock plus feedback and correction. Rotate coding (I5, I8, I11), practical debugging (I6, I9), system design (I7, I10), and a project/behavioral interview (I12). In I4, book the dates with the reviewer you named in E4; if no human interviewer is available, record the rehearsal and obtain feedback before counting the readiness check as passed. The mocks from I8 on are also the readiness evidence.
For debugging, use a small unfamiliar public repository or a peer-prepared change: reproduce → investigate → patch → regression test → explain. Do one attempt unaided. If your target interview allows AI, use the next debugging mock to practise bounded AI assistance and independent verification. Ask the recruiter which tools are allowed; do not assume every interview permits them.
Communication and experience — 1 hour each week
Explain one technical decision aloud and practise stories about a bug, a trade-off, collaboration or feedback, and ownership of a result. State the situation, your action, evidence of the outcome and what you would change. Use actual work, the shop or the v0 demo; distinguish measured results from assumptions. From I9, use part of this hour for a concise CV and a five-minute project walkthrough.
Weekly routine
| Stage | Weekly allocation | Total |
|---|---|---|
| E1–E13 | Weekdays: 5 hours reading and small exercises. Weekend: 5 hours building, testing and reviewing the current layer; from E5 to E11 the last 45 minutes are the agent replay. The first assessment and the posting scan use 2½ of E1's hours. | 10 hours |
| I1–I12 | 4 hours coding + 2 hours fundamentals/design + 3 hours practical work/mocks and corrections + 1 hour communication | 10 hours |
AI: two modes and a data boundary
Know which mode you are in before you open an AI tool. Part 2 keeps both modes and adds its own limits.
E5–E11, the agent replay. Once your own layer is working and committed, cut a branch from the commit before it and have an AI agent build the same layer, or one bounded part of it: write the task, give it the context, run your layer's tests against its branch, and read its diff beside yours. Log in
ai/agent-log.md what it got wrong, what it did better and what your task left out. Never merge that branch. About 45 minutes, taken from the weekend's five hours; if the layer is not working by then, the layer wins and that week's replay is skipped, not owed.I2 and I4, the real change. Make that week's shop change with the agent instead of by hand: write the task, give it the context, review the diff, merge only what you can explain, and log it the same way.
If the agent is billed by usage, set a hard spend limit before its first run in E5, as E13 does for the LLM API. In interviews, practise it only in a debugging mock whose target interview allows AI. It is the one AI skill that pays off whether or not you move into ML.
Ready for Part 2 when
Collect this evidence from the second round of mocks: coding in I8 and I11, debugging in I9, design in I10 and the project interview in I12. If I9 was your AI-assisted debugging mock, the unaided attempt in I6 is the one that counts for item 2. These are local readiness criteria for the roles you selected, not a guarantee of passing every employer's interview. A job offer is not required.
- Build and operate: a reviewer can run the shop and its tests from the README. You demonstrate authorization, duplicate-request handling, concurrent stock correctness, one measured performance fix and the checked restore. Explain the limitations.
- Debug and change code: in an unfamiliar-code exercise, reproduce a failure, explain its cause, make a small fix and show a regression test. At least one reviewed attempt is unaided; documentation is allowed.
- Solve coding problems: in two separate reviewed mocks, solve an unseen problem appropriate to the target role without hints, explain complexity and test edge cases. Repeated solutions from memory do not count. If the target uses medium problems, practise and assess at that level.
- Explain a system: complete a 35–45 minute design discussion covering requirements, API, data model, a failure scenario and a justified trade-off. Explain one SQL query and why its result and access path are correct.
- Communicate your work: give the five-minute project walkthrough and concrete examples of debugging, ownership and collaboration; another engineer's material feedback has been addressed.
Applications are optional; preparation is part of Part 1
After the readiness check, choose whether to apply for software/backend roles, start Part 2, or continue building engineering experience. You do not need ML projects to finish this plan.
If you start applying, use ten hours as a cap: 4 hours applications and interviews, 3 hours coding, 2 hours practical/design refresh and 1 hour communication. A take-home or extra interview replaces those practice hours; extend the calendar if necessary. Refresh your target postings and confirm each employer's interview format.
If you start Part 2, its Week 0 opens with a fresh market scan and a decision about order — the Applied AI weeks can run before the ML theory — and its budget includes maintenance practice. If a search begins during Part 2, pause ML or explicitly give it only the hours left after interview work. Before a later search, run fresh mocks and refresh the weak areas; interview readiness is not permanent.
Progress checkpoints
v0 demo public, tagged and frozen, with its known limitations; API spend cap set.Next action: schedule the first 60-minute endpoint assessment from the overview.