Contents
  1. Background: why a study list is not a plan
  2. The planning pipeline
  3. Building the baseline
  4. The priority formula
  5. Auditing the plan against reality
  6. The plan, and what happened to it
  7. Results
  8. What I’d do differently
  9. Conclusion

The first honest look at my readiness for a Google loop took about twenty minutes. It was a 28-question self-assessment across dynamic programming, graphs, heaps, sliding windows, SQL, ML theory, statistics and LLMs. The results were stark. Dynamic programming: five problems, five rated “can’t start.” Statistics and A/B testing: four topics, four rated “can’t start.” SQL retention patterns: zero. Large language models (LLMs): every item rated interview ready, because I had spent the previous year building LLM systems in production.

That profile is common among data scientists who move toward ML engineering. Production work makes you strong where production lives, and leaves holes exactly where interview loops like to probe. The question was not whether I had gaps. It was which of them would cost me an offer, and in what order to close them.

This post describes how I turned that baseline into a ranked plan: a pipeline of planning documents, a priority formula, an audit against real candidate reports, and a calendar that had to be rebuilt each time the interview date moved. It is part 2 of the 35 companies, 8 offers series.

For context, the loop was for a Software Engineer III role in AI/ML at Google in India. The recruiter described four 45-minute rounds: an ML domain round, a Googleyness and Leadership (G&L) round, and two back-to-back coding rounds held in person, each with a medium and a hard problem, written in Google Docs with no IDE. Planning began in late March; the loop finished on July 29.

Where the plan took me:

  • Dynamic programming: 0 of 5 problems startable → framework rated 3 out of 5 a few days later → closed by June
  • ML implementation from scratch: rated 2.5 out of 5 and ranked the top risk → ML domain round at Strong Hire
  • Behavioral delivery: rated 2.5 out of 5 → G&L round at Strong Hire
  • Coding rounds: recruiter-flagged weak areas → both rounds at Hire

Background: why a study list is not a plan#

My first instinct was the one most people have: make a list of topics and start working through it. Three problems made that approach fail before it began.

The topic list was unbounded. An ML engineering loop at Google can touch classical ML, deep learning, LLMs, recommendation systems, statistics, system design, coding and behavioral questions. Every guide I read added more. A list without a stopping rule grows faster than you can study it.

My ratings came from memory. I knew I had solved coin change and longest increasing subsequence at some point. I did not know whether I could solve them cold, under time pressure, while talking. Self-ratings that come from memory are known to be unreliable in exactly the areas where skill is weakest; Kruger and Dunning (1999) found that people with the least skill in a domain overestimate their performance the most.

The loop had two unknowns. I did not know which team I was interviewing for or who my interviewers would be. An ML domain round for a recommendations team looks different from one for an ads or a health team, and the recruiter could not say yet.

A plan has to answer three questions a list cannot: what to skip, what order to work in, and how you will know a gap is closed.

The planning pipeline#

I built the plan in stages, each stage a separate conversation with Claude that produced one document, and each document the input to the next. I called the top-level file a “plan to plan.” It described the steps and never contained prep content itself.

graph LR
    A["Context capture"] --> B["Resolve unknowns"]
    B --> C["Topic universe"]
    C --> D["Baseline ratings"]
    D --> E["Priority map"]
    E --> F["Day-by-day plan"]
    F --> G["Round playbooks"]
    G --> H["Mocks and drills"]
    H -->|"cold results"| D
    C -.->|"audit vs reports"| C

    class A,B gray
    class C,D,E amber
    class F,G violet
    class H green
Figure 1: The planning pipeline. Each stage was one document; results from mocks fed back into the baseline, and the topic universe was audited against real candidate reports.

Context capture. Everything the recruiter had told me went into one place: four rounds, 45 minutes each, coding in Google Docs, solutions reviewed by senior engineers afterwards. The recruiter also named the areas where candidates at this level most often fail: dynamic programming, graph traversal, trees and recursion. The evaluation criteria were explicit too: an optimal solution with no hints, thinking out loud, dry-running the code, clean code, edge cases, and time and space complexity.

Resolving the unknowns. Since team and interviewer were unknown, I covered the most common team archetypes (recommendations and search, ads, conversational AI, ML platform, maps, health) and weighted topics by how often they appeared across all of them.

The topic universe. This was an exhaustive inventory, built from recent public candidate reports for ML roles in India: interview write-ups on Glassdoor, Blind and LeetCode Discuss, and preparation guides. Every topic got a confidence tag: confirmed in a report, likely, or long tail.

Baseline, priority map and plan. These three are the subject of the rest of this post.

Building the baseline#

A useful baseline is not one number per subject. It is a rating per topic with the specific error pattern attached, because the error pattern is what you drill.

I rated each topic on a 1 to 5 scale: 1 meant never attempted, 3 meant knows the pattern but makes errors under pressure, 5 meant solves it cold in interview conditions. Dynamic programming is the clearest example of why the error pattern matters. A few days after the “can’t start” assessment, I spent one long Socratic session working through seven classic problems, one question at a time, attempting each before getting any correction. By the end the framework (define the state, write the transition, set the base cases) was solid. What remained were five recurring mistakes:

  1. Writing i - c > 0 where the condition needed i - c >= 0.
  2. Iterating over indices instead of coin values in the inner loop of coin change.
  3. Mixing a 1-indexed DP array with a 0-indexed input.
  4. Adding 1 when combining path counts, where nothing was being extended.
  5. Starting a loop at zero and overwriting a base case.

That changed the rating from 0 to 3, and it changed the next step. I did not need more DP theory. I needed cold attempts that tested whether those five errors were gone.

The table below shows a slice of the baseline as it stood in early April. The full document rated several dozen topics this way.

Area Rating (1–5) Priority
Recommendation system design 5 Strength, maintain only
Behavioral story content 4.5 Strength, maintain only
Dynamic programming 3 P0
Graphs, including Dijkstra under time pressure 3.5 P0
ML implementation: two-tower and dual-encoder 2.5 P0
ML implementation: CNN, batch norm, layer norm 2 P0
Behavioral delivery and framing 2.5 P0
Backtracking 2 P1
Monotonic stack 2.5 P1
Trie 2 P2

The strengths matter as much as the gaps. Marking an area “maintain only” is a commitment not to spend time there, and that commitment is where most of the time savings came from.

The priority formula#

Ranking dozens of topics by gut feel produces a ranking that tracks whatever you studied last. I used a simple scoring rule instead:

Priority = probability of appearing × (1 / current rating) × fixability in the time left.

Probability came from the topic universe. Rating came from the baseline. Fixability was a judgment call: backtracking can go from 2 to 4 in a few sessions; distributed training cannot. The product sorted every topic into four buckets.

graph TD
    A["Topic universe"] --> D["Score each topic"]
    B["Baseline rating"] --> D
    C["Weeks remaining"] --> D
    D --> E["P0: can cause No Hire"]
    D --> F["P1: costs Strong Hire"]
    D --> G["P2: if time permits"]
    D --> H["Do not re-study"]

    class A,B,C blue
    class D amber
    class E red
    class F violet
    class G gray
    class H green
Figure 2: The priority map. Three inputs scored every topic into four buckets, including an explicit list of strengths not to revisit.

Three findings overrode everything else, and I wrote them at the top of the priority map as forcing functions.

ML implementation was a gate, not a topic. One candidate report I found described a candidate who earned Strong Hire on both coding rounds and on G&L, then received a Lean No Hire on the ML domain round because they could not implement a dual-encoder model mid-interview. The design discussion had been fine. Yuan Meng’s overview of modern ML engineering interviews makes the same point from the other side: the ideal candidate can write common architectures and their building blocks from memory. My implementation rating was 2.5. That made it the single highest-risk item in the plan.

Delivery would cost a Hire regardless of content. My stories rated 4.5 on content and 2.5 on delivery: I buried conclusions after long setup, said “I single-handedly” where Google looks for collaboration, and filled pauses with “yeah, so.” No amount of writing better stories fixes delivery. Only spoken practice does.

Mediums had to take under 20 minutes. Each coding round had a medium and a hard problem in 45 minutes. If the medium took 30, the hard one was gone. Speed on mediums was a prerequisite, not a bonus.

Auditing the plan against reality#

The first version of the topic universe had a mistake that would have cost me. It listed backtracking as “not confirmed for ML roles.” An audit against more candidate reports found backtracking confirmed in a 2025 Bengaluru ML report and named as must-not-miss in an offer write-up. It moved up two priority tiers.

That audit pass, done twice in the first week, changed the plan more than any other step. It added monotonic stacks, convolution and batch normalization from scratch, on-device model design, position-bias correction in ranking, and the multi-stage ranking vocabulary interviewers expect (candidate generation, then successive ranking stages). It also flagged every baseline rating that came from memory rather than a cold attempt, with a date to verify each one.

I also changed how I represented frequency. Public guides quote precise percentages; IGotAnOffer’s analysis of 100 recent Google coding questions reported on Glassdoor puts arrays and strings at 42.9%, graphs and trees at 33.7% and dynamic programming at 11.2%. Other aggregates disagree on the exact numbers while agreeing on the order. I kept relative bands (large, medium, small) and stopped pretending to a precision the data did not have.

The plan, and what happened to it#

The day-by-day plan assumed four weeks at ten hours a day, about 280 hours, with a fixed daily shape: coding in the morning, ML domain at midday, statistics or behavioral in the afternoon, a debrief or spoken practice in the evening, and thirty minutes to plan the next day. The weeks had names. Week 1 was “eliminate zeros”: every P0 gap got its first full session, with no mocks. Week 2 was “build fluency.” Week 3 was “interview mode,” with timed mocks. The first mock was not allowed before day 10.

Then the date did not come. The loop I had planned for in April did not get scheduled until an invite arrived on May 31, for June 24. The dates then moved several more times before settling.

graph LR
    A["Late Mar<br/>plan for April"] --> B["May 31<br/>invite for Jun 24"]
    B --> C["Jun<br/>domain Jul 6"]
    C --> D["Jul<br/>domain Jul 9"]
    D --> E["G&L Jul 15"]
    E --> F["Coding Jul 29"]

    class A gray
    class B,C,D violet
    class E,F amber
Figure 3: How the interview dates moved. Each change forced a rebuild of the calendar, but not of the priority map.

This is where the pipeline earned its keep. Every date change meant a new calendar, but the priority map underneath stayed valid, because it ranked topics by risk rather than by date. Rebuilding the calendar took an hour. Rebuilding the priorities would have taken days.

By early June the baseline had a “closed” section: dynamic programming (coin change, longest increasing subsequence, longest common subsequence, word break, edit distance, all solved in one pass with no hints), intervals and two pointers. ML implementation was still the highest-risk area, now with a specific list: backpropagation in NumPy, logistic regression, k-means, attention, Adam, convolution and batch norm. When the coding rounds were finally fixed for late July, the same method produced an 18-day sprint over the patterns still open, which gets its own post.

Results#

The outcomes of the loop map closely to the priority map.

Priority area Baseline Outcome
ML implementation and theory 2.5, ranked top risk ML domain round: Strong Hire
Behavioral delivery 2.5 G&L round: Strong Hire
Dynamic programming, graphs and other patterns 0–3.5 across topics Both coding rounds: Hire
Recommendation system design 5, maintain only Used throughout, never re-studied

The ML domain round included live implementation from scratch, the item I had ranked first. The G&L round rewarded conclusion-first, specific answers, which was the delivery work rather than the story work. The coding rounds were both Hire rather than Strong Hire, which fits the area where I started furthest behind.

The less visible result was time. The “maintain only” list covered my strongest areas, and I spent almost nothing on them. Without that list, I would have spent comfortable hours polishing recommendation system designs I already knew.

What I’d do differently#

Rate cold from day one. The memory-based baseline was wrong in both directions: some “drilled” problems failed cold, and some topics I had rated low turned out to be fine. A two-hour cold test of one problem per topic would have saved a week of recalibration.

Plan in phases with exit gates, not calendar days. A four-week day-by-day plan assumes the date holds. Mine moved repeatedly. A plan written as “phase 1 ends when these five problems are solved cold” survives a moved date without a rewrite.

Do fewer documents. The pipeline worked, but by June I had more than a dozen source documents and several patches on top of them. A smaller set with stricter scope would have been easier to keep consistent.

Budget hours honestly. The plan assumed ten hours a day. That held while I was between roles and stopped holding once I started a new one. A plan should be built on the hours you will have in the worst week, not the best one.

Conclusion#

A baseline tells you where you are; a priority map tells you what to ignore. The second matters more, because the scarce resource in interview preparation is not material but time, and every hour spent on a strength is an hour taken from a gap that can sink the loop.

The formula is simple enough to apply to any loop: probability of appearing, divided by current rating, multiplied by how fixable the gap is in the time you have. Rank by risk, not by date, and a moving date will cost you an hour instead of a week.

Part 2 of the 35 companies, 8 offers series. I join Google as a Software Engineer III, AI/ML, in October 2026.