Software Engineer
Software Engineer Roadmap and Interview Guide
This page is a complete roadmap for becoming a software engineer and passing software engineering interviews, from your first program to a staff-level loop. It covers programming foundations, computer science, data structures and algorithms, coding interview technique, professional engineering practice, building real backend systems, low-level design, system design and behavioural interviews. Every topic is explained here so the page stands on its own; where a subject has its own detailed skill roadmap, we summarise it and link to it.
It suits computer science students, self-taught developers, bootcamp graduates, engineers in other fields moving into software, and working engineers preparing for a move up a level. If you are just starting, work through the stages in order. If you already work as an engineer, use the interview loop, levels and 12-week plan to focus on the rounds that decide your target level.
- Level
- Beginner to senior and staff
- Time
- 9–15 months from zero at about 12 hours a week; 10–12 weeks of focused interview preparation if you already write code professionally
- Scope
- 57 topics in 9 stages
- Before you start
- School-level maths: algebra, basic logarithms and exponents, and comfort with simple proofs by example; A computer that can run a code editor, Git, Docker and your chosen language; No prior programming experience is needed; the first stage starts from zero
What you will be able to do
- Write clean, tested code in one main language and debug unfamiliar codebases
- Choose the right data structure and algorithm for a problem and state its time and space complexity
- Solve medium coding interview problems in 25–35 minutes using a repeatable framework, while explaining your reasoning
- Design classes and interfaces for a low-level design problem using SOLID principles and common patterns
- Design a scalable, reliable distributed service and defend its trade-offs in a system design round
- Build, test and deploy a backend service with a database, a clear API, authentication and caching
- Present your experience in structured behavioural answers pitched at your target level
What a Software Engineer does
A software engineer designs, builds, tests and maintains software. That might be the backend of a payments platform, a mobile app, a search engine, the firmware in a car or the internal tools a company runs on. The work is less about typing code than about turning a vague need into a precise design, choosing sensible trade-offs, writing code that others can read and change, and keeping it working once real users depend on it. Engineers spend a large part of each week reading code, reviewing colleagues' changes, writing design notes, debugging and talking to product managers, designers and other teams.
The title is broad. Some engineers specialise early in front end, back end, mobile, infrastructure, data or machine learning; others stay generalists. Large technology companies hire most engineers through a common interview loop of coding, design and behavioural rounds and decide the team later, which is why general preparation in data structures and algorithms, system design and communication pays off whichever area you end up in.
A typical week
- Break a feature or bug into small tasks, write the code and its tests, and open a pull request
- Review teammates' pull requests for correctness, design and readability, and respond to review on your own
- Discuss requirements and edge cases with product managers, designers and other engineers
- Write or comment on design documents for upcoming features and changes to shared systems
- Investigate production issues using logs, metrics and traces, and take part in an on-call rotation
- Improve existing code: refactor, remove technical debt, speed up slow paths and add missing tests
What is expected at each level
New grad or Junior
- Delivers well-scoped tasks with guidance, writing correct, tested code that follows team conventions
- Learns the codebase, tools and deployment process quickly and asks good questions early
- Debugs their own code and simple production issues with help, and responds well to review feedback
- Interviews focus on coding rounds (data structures, algorithms, clean code) and basic behavioural questions
Mid-level
- Owns features end to end, from clarifying requirements to design, implementation, rollout and monitoring
- Makes sound local design decisions and writes short design docs for medium-sized changes
- Reviews others' code thoroughly, takes part in on-call and resolves incidents in their area
- Interviews add a system design or low-level design round and expect stories of independent ownership
Senior
- Leads projects that span several engineers or services, breaking ambiguous goals into a delivery plan
- Designs systems with clear trade-offs on scale, reliability, cost and maintainability, and writes the key design docs
- Mentors other engineers, raises the quality bar through reviews and improves team practices
- Interviews weigh system design and behavioural rounds heavily, looking for leadership, judgement and handling of conflict
Staff or Principal
- Sets technical direction for a group of teams or a whole area, aligning it with business goals
- Solves problems that cross team boundaries, such as platform migrations, reliability programmes or shared architecture
- Influences without authority through writing, reviews and relationships, and grows senior engineers
- Interviews include deep architecture discussions, a past-project deep dive and behavioural questions about organisation-wide impact
The interview loop
- Recruiter screen20–30 min
What it tests. Your background, motivation, target level, location and work authorisation, salary expectations and timeline. The recruiter also explains the loop.
How to prepare. Have a two-minute summary of your experience and one project ready. Ask about the number of rounds, allowed languages, whether low-level or system design is included, and the level being considered.
- Online assessment or technical phone screen45–90 min
What it tests. One or two coding problems, usually easy to medium, solved in a shared editor or an automated test platform. Correctness, efficiency, code quality and communication.
How to prepare. Practise timed problems in coding practice across all common patterns. For live screens, rehearse thinking aloud; for automated tests, practise reading constraints and handling edge cases that hidden tests will check.
- Coding rounds (onsite or virtual)2 × 45–60 min
What it tests. Medium and sometimes hard algorithm problems, often with follow-ups that change constraints. Problem solving, choice of data structures, clean code, testing and how you use hints.
How to prepare. Use the seven-step framework on every practice problem. Do several mock interviews with a person watching. Review your pattern log so cues come quickly under pressure.
- Low-level or object-oriented design45–60 min
What it tests. Turning a problem such as a parking lot or a booking system into classes, interfaces and relationships; applying SOLID and patterns; extensibility and sometimes concurrency. Common for junior to mid-level roles and at some companies for all levels.
How to prepare. Design ten classic LLD problems in code in your interview language. Practise explaining entities, relationships and one use case walk-through within 45 minutes.
- System design45–60 min
What it tests. Designing a scalable service end to end: requirements, estimates, API, data model, architecture and deep dives. Usually from mid-level upwards and decisive for senior and staff levels.
How to prepare. Learn the building blocks, then practise one classic problem each week in system design practice, out loud and timed. Prepare to go deeper into one area you know from real work.
- Behavioural or hiring manager round45–60 min
What it tests. Collaboration, ownership, conflict, handling failure, growth and fit with the team. For senior and staff roles, scope of impact, leadership and influence. Often includes a deep dive into a past project.
How to prepare. Prepare eight to ten STAR stories mapped to the company's values, with scope matching your target level. Rehearse one project deep dive including the design, trade-offs, what went wrong and what you would change.
Skill roadmaps for this role
Each skill below has its own complete roadmap. Learn the core ones first.
- CoreComputer Science Roadmap
Computer science fundamentals explain why code behaves as it does and underpin follow-up questions in every technical round.
- CoreData Structures and Algorithms Roadmap
Data structures and algorithms are the core of coding rounds at almost every company that hires software engineers.
- CoreGit and GitHub Roadmap
Git is how every team shares and reviews code, and you will use it daily from your first week.
- CoreSQL Roadmap
Most applications store data in relational databases, and SQL appears in practical tasks and system design discussions.
- CoreAPI Design Roadmap
Services talk through APIs, and clear API design is tested in system design and practical rounds.
- CoreSoftware Design and Architecture Roadmap
Design principles and patterns are what low-level design rounds test and what keeps real codebases maintainable.
- HelpfulCode Review Roadmap
Reviewing code well is a daily habit and a common behavioural topic for mid-level and senior engineers.
- CoreSystem Design Roadmap
System design decides the level you are hired at from mid-level upwards and is the main senior and staff round.
The Software Engineer roadmap
- Stage 1Programming foundationsBeginner · 4–6 weeks · 7 topics
- Stage 2Computer science fundamentalsBeginner · 3–5 weeks · 4 topics
- Stage 3Data structures and algorithmsIntermediate · 8–12 weeks · 9 topics
- Stage 4Coding interview techniqueIntermediate · 6–10 weeks, overlapping with the previous stage · 6 topics
- Stage 5Professional engineering practiceIntermediate · 4–6 weeks · 7 topics
- Stage 6Building real systemsIntermediate · 6–8 weeks · 6 topics
- Stage 7Low-level design and object-oriented designAdvanced · 3–5 weeks · 5 topics
- Stage 8System designAdvanced · 6–8 weeks · 8 topics
- Stage 9Behavioural interviews, levelling and the job searchIntermediate · 2–4 weeks, in parallel with technical preparation · 5 topics
Stage 1 Programming foundations
Goal. Write, run, test and debug small programs in one language with confidence, and work comfortably from the command line.
Choosing your first language Core
Pick one general-purpose language and stay with it until you can solve problems without looking up syntax. Python is the easiest to read and the most common choice for coding interviews because it is short to write. Java and C# are typed, verbose but very common in large companies, and they make object-oriented design explicit. JavaScript or TypeScript make sense if you want to build web products. Go is popular for backend services, and C++ is still standard in games, trading and systems work.
The language matters far less than depth. Interviewers rarely care which mainstream language you use, but they notice when you fight it. Learn its standard collections (list, map, set, queue, heap), string handling, error handling and how to write and run tests.
Practice. Write the same 50-line program, such as a word-frequency counter for a text file, in your chosen language, then rewrite it using only standard library collections.
Variables, control flow and functions Core
Every program is built from the same pieces: values stored in variables, decisions with if, repetition with loops, and functions that take inputs and return outputs. Learn the difference between primitive values and references, because it explains many beginner bugs: in most languages, passing a list to a function lets the function change the caller's list.
Write small functions that do one thing and have clear names. Prefer returning values over printing them, so the result can be tested. Understand scope (where a variable is visible), recursion (a function calling itself with a smaller input) and how to handle errors with exceptions or returned error values. These basics carry straight into coding interviews, where clean decomposition into helper functions is rewarded.
Practice. Implement FizzBuzz, a palindrome checker and a recursive factorial, each as a function with three test cases.
How code runs: memory, types and the call stack Core
Know what happens when your code runs. Compiled languages such as C++, Go and Rust turn source into machine code before running; Java and C# compile to bytecode run by a virtual machine; Python and JavaScript are interpreted or just-in-time compiled. Static typing catches some mistakes before running, dynamic typing gives flexibility at the cost of runtime errors.
Each function call pushes a frame onto the call stack holding its local variables; deep recursion can overflow it. Objects that outlive a call live on the heap and are freed by a garbage collector, by reference counting, or by hand in C and C++. Understanding stack versus heap, integer overflow, floating-point rounding and immutable strings helps you reason about bugs and about the space complexity of your solutions.
Practice. Write a recursive function that sums 1 to n and find the n at which it overflows the stack in your language, then rewrite it iteratively.
Object-oriented and functional basics Core
Most production code mixes two styles. Object-oriented programming groups data with the behaviour that acts on it. Learn classes and objects, encapsulation (hide internal state behind methods), inheritance, interfaces and polymorphism (calling the same method on different types). Prefer composition, where one object holds another, over deep inheritance trees.
Functional programming favours pure functions with no side effects, immutable data and functions passed as values. Map, filter and reduce, lambdas and closures appear in every modern language. Pure functions are easy to test and safe to run in parallel. You need both styles for low-level design interviews, where you model a system with classes, and for everyday code, where small pure helpers keep logic clear.
Practice. Model a library with Book, Member and Loan classes, then write a pure function that returns overdue loans from a list.
Testing your own code Core
Professional engineers prove their code works with automated tests. A unit test calls one function with known inputs and checks the output. Learn your language's test framework (pytest, JUnit, Jest, Go's testing package) and write tests for the normal case, edge cases (empty input, one element, duplicates, very large values) and error cases.
Testing habits also win coding interviews. After writing a solution, strong candidates walk through a small example by hand, then name the edge cases and check them. Test-driven development, writing the failing test first, is worth trying because it forces you to define behaviour before writing code. Good tests are fast, independent of each other and named after the behaviour they check.
Practice. Write a function that merges two sorted lists and cover it with at least eight unit tests, including empty and duplicate cases.
Debugging and reading code Core
Most engineering time goes on reading and fixing existing code, not writing new code. Learn to use a debugger: set breakpoints, step through lines, inspect variables and the call stack. Use print or log statements when a debugger is awkward. Read stack traces from the bottom of your own code upwards to find where things failed.
Debug systematically: reproduce the bug reliably, form a hypothesis, test it with the smallest change, and narrow the search by halving (comment out half, or use git bisect to find the commit that broke it). When reading an unfamiliar codebase, start from an entry point, follow one request through, and write down what each module does. These skills are tested in some interviews through debugging rounds.
Practice. Clone a small open-source project, run its tests, deliberately break one function and use the debugger and test output to locate it again.
Command line and Linux basics Core
Servers run Linux and most developer tools are command-line programs. Learn to move around the file system (cd, ls, pwd), manage files, read files with cat, less and tail -f, search with grep and find, and chain commands with pipes. Understand file permissions, environment variables, processes (ps, top, kill) and how to install packages.
Learn one editor well, including its search, refactoring and debugging features, and know enough of a terminal editor such as Vim or nano to edit a file on a remote server over SSH. Writing small shell scripts to automate repetitive tasks pays back quickly. The Linux Roadmap and Shell and Bash Roadmap go further.
Practice. Use one pipeline of grep, sort and uniq to find the ten most frequent IP addresses in a sample web server log.
Stage 2 Computer science fundamentals
Goal. Explain how programs use the CPU, memory, operating system and network, and analyse the cost of code with Big-O notation.
Big-O and complexity analysis Core
Big-O gives the growth rate of an algorithm's cost as its input gets larger, with constant factors dropped. Learn the common classes in order: O(1) constant, O(log n) such as binary search, O(n) a single pass, O(n log n) good sorting, O(n²) nested loops over the same data, O(2ⁿ) trying every subset and O(n!) every ordering.
Analyse both time and space, and the worst, average and amortised cases. A dynamic array append is O(1) amortised because occasional O(n) resizes are spread over many cheap appends. Hash map lookups are O(1) on average but O(n) in the worst case. Use input limits to guess the target: n up to 10⁵ usually needs O(n log n) or better, n up to 20 allows exponential search. In interviews, always state the complexity of your solution without being asked.
Practice. Take ten short functions, write the time and space complexity of each, then confirm two of them by timing runs at n = 10³, 10⁴ and 10⁵.
Operating systems: processes, threads and concurrency Core
An operating system shares the machine between programs. A process has its own memory space; a thread runs inside a process and shares its memory with other threads. Context switching between them has a cost. Virtual memory gives each process the illusion of its own address space, backed by pages that can be swapped to disk.
Concurrency bugs come from shared mutable state. A race condition happens when the result depends on timing, for example two threads incrementing the same counter. Protect shared state with locks (mutexes), atomic operations or by not sharing at all (message passing, immutable data). Know deadlock (two threads each holding a lock the other needs) and how to avoid it with a consistent lock order. Also know the difference between concurrency and parallelism, and between threads and async I/O with an event loop. The Computer Science Roadmap covers operating systems, compilers and the rest of the theory in depth.
Practice. Start four threads that each increment a shared counter 100,000 times, observe the wrong total, then fix it with a lock and with an atomic integer.
Networking basics Core
Most software talks over a network, so know the layers. IP moves packets between machines using addresses. TCP adds reliable, ordered delivery with a connection handshake; UDP is connectionless and faster but may lose packets, which suits video calls and games. DNS turns names into IP addresses. HTTP is the request and response protocol of the web, and HTTPS wraps it in TLS for encryption and server identity.
Learn HTTP methods, status codes (200, 201, 301, 400, 401, 403, 404, 429, 500, 503), headers, cookies and keep-alive connections. The classic question 'what happens when you type a URL into the browser' tests this whole chain, from DNS lookup to TCP and TLS handshakes, the HTTP request, the server and rendering.
Practice. Use curl with verbose output to request a website, and label each stage you see: DNS, connect, TLS, request headers, response.
Databases fundamentals Core
A relational database keeps each kind of record in its own table, connects tables through keys and answers questions written in SQL. Learn SELECT with WHERE, JOIN, GROUP BY, ORDER BY and LIMIT, plus INSERT, UPDATE and DELETE. Normalisation removes duplicated data; denormalisation adds it back for faster reads.
An index, usually a B-tree, makes lookups on a column O(log n) instead of a full scan, at the cost of slower writes and extra storage. A transaction groups changes so they all apply or none do; ACID stands for atomicity, consistency, isolation and durability. Know when a non-relational store fits better: key-value stores for caching, document stores for flexible records, wide-column stores for very high write volume. The SQL Roadmap goes deeper into queries and data modelling.
Practice. Design tables for users, posts and comments, write the query for the five most commented posts this week and add the index it needs.
Stage 3 Data structures and algorithms
Goal. Choose and implement the right data structure for a problem, and apply core algorithms with correct complexity.
Arrays and strings Core
Arrays give O(1) access by index and are cache friendly; inserting or deleting in the middle costs O(n) because elements shift. Dynamic arrays (Python list, Java ArrayList, C++ vector) grow by doubling. Strings are arrays of characters and are immutable in many languages, so building a string in a loop with + can be O(n²); use a builder or join a list instead.
Core techniques: prefix sums to answer range-sum queries in O(1), in-place reversal and swapping, sorting first to make later steps easier, and counting characters with a fixed-size array for anagram problems. Most interview problems start as arrays or strings, so be fluent with indices, off-by-one boundaries and slicing costs in your language.
Practice. Solve: given an array, return a new array where each element is the product of all other elements, without division, in O(n).
Hash maps and sets Core
A hash map maps each key to a value with O(1) average insert, lookup and delete. It works by hashing the key to a bucket index; collisions are handled by chaining or open addressing, and the table resizes when it gets too full. A set is a hash map without values. Keys must be hashable and should not change after insertion.
Hash maps are the single most useful interview tool. They turn O(n²) 'check every pair' solutions into O(n): store what you have seen and check for the complement (two-sum), count frequencies, group items by a computed key (group anagrams by sorted letters) and remember earlier results (memoisation). Know that ordered maps (tree maps) give O(log n) operations but keep keys sorted, which helps for range queries.
Practice. Solve two-sum, group anagrams and longest consecutive sequence, each in O(n) average time using a map or set.
Linked lists, stacks and queues Core
A linked list stores nodes that point to the next node, allowing O(1) insertion and removal when you already hold the node, but O(n) access by position. Interview favourites: reverse a list, detect a cycle with fast and slow pointers, find the middle, merge two sorted lists. Use a dummy head node to avoid special cases.
A stack is last in, first out; use it for matching brackets, undo history, evaluating expressions and simulating recursion. A queue is first in, first out and drives breadth-first search. A deque supports both ends and powers sliding-window maximum. A monotonic stack, kept in increasing or decreasing order, answers 'next greater element' questions in O(n).
Practice. Implement valid parentheses, reverse a linked list iteratively and recursively, and daily temperatures with a monotonic stack.
Trees and binary search trees Core
A tree is a hierarchy of nodes with one root and no cycles. Binary trees have at most two children. Learn the traversals: pre-order, in-order and post-order (depth-first, usually recursive) and level-order (breadth-first with a queue). Many tree problems are solved by a recursive function that returns information from each subtree to its parent, such as height, a sum or whether it is balanced.
A binary search tree keeps smaller keys on the left and larger on the right, so in-order traversal yields sorted keys and search is O(h). Unbalanced trees degrade to O(n); self-balancing trees such as red-black and AVL keep the height O(log n). A trie stores strings by character and supports fast prefix search, useful for autocomplete questions.
Practice. Solve maximum depth, validate a BST, lowest common ancestor and level-order traversal, then implement a trie with insert and startsWith.
Heaps and priority queues Core
A binary heap keeps the smallest (or largest) element at the top. Push and pop are O(log n), peek is O(1), and building a heap from n items is O(n). Most languages provide one: heapq in Python, PriorityQueue in Java, priority_queue in C++.
Reach for a heap when a problem says 'k largest', 'k closest', 'merge k sorted' or 'schedule the next task'. Keep a heap of size k to find the top k in O(n log k). For a running median, keep the smaller half of the numbers in a max-heap and the larger half in a min-heap, and rebalance after each insert. Heaps also drive Dijkstra's shortest path algorithm and many scheduling simulations.
Practice. Solve kth largest element, merge k sorted lists and find median from a data stream.
Graphs: BFS, DFS and beyond Core
Graphs model networks of things: cities and roads, users and friendships, tasks and dependencies. Represent them with an adjacency list (map from node to neighbours). Grids are graphs too, with up to four neighbours per cell. Breadth-first search explores in layers with a queue and gives the shortest path in an unweighted graph. Depth-first search goes deep with recursion or a stack and suits counting connected components and detecting cycles. Always track visited nodes.
Next learn topological sort for ordering tasks with dependencies, Dijkstra's algorithm for weighted shortest paths with non-negative edges, and union-find (disjoint set union) for merging groups and detecting cycles in undirected graphs.
from collections import deque
def bfs(start, graph):
dist = {start: 0}
q = deque([start])
while q:
node = q.popleft()
for nxt in graph[node]:
if nxt not in dist:
dist[nxt] = dist[node] + 1
q.append(nxt)
return dist
Practice. Solve number of islands with both BFS and DFS, then course schedule with topological sort.
Sorting and binary search Core
Know how merge sort (O(n log n), stable, extra memory), quicksort (O(n log n) average, O(n²) worst, in place) and heap sort work, and when counting sort or bucket sort beat them for small integer ranges. In interviews you normally call the built-in sort, but you should know its complexity and how to sort with a custom key.
Binary search finds a target in a sorted array in O(log n). Its bigger use is 'binary search on the answer': when you can test whether a value works and the test is monotonic (if speed x works, so does any faster speed), search the space of answers. Watch the boundaries: decide whether you search a closed or half-open range and keep the loop invariant consistent.
def first_true(lo, hi, ok):
# smallest x in [lo, hi) with ok(x) True
while lo < hi:
mid = (lo + hi) // 2
if ok(mid):
hi = mid
else:
lo = mid + 1
return lo
Practice. Implement merge sort from scratch, then solve search in rotated sorted array and minimum eating speed using binary search on the answer.
Recursion and backtracking Core
Recursion solves a problem by solving smaller copies of it. Every recursive function needs a base case and progress towards it. Draw the recursion tree to understand cost: a function that calls itself twice on n-1 is O(2ⁿ).
Backtracking builds a solution step by step and undoes a choice when it leads nowhere. It generates subsets, permutations and combinations, and solves constraint puzzles such as N-queens, Sudoku and word search. The template is: choose, recurse, un-choose. Prune early when a partial solution cannot succeed, which often turns an impossible search into a fast one. State the exponential complexity honestly; interviewers expect it.
Practice. Generate all subsets, all permutations and all combinations summing to a target, using one shared backtracking template.
Dynamic programming Core
Dynamic programming (DP) applies when a problem has overlapping subproblems and optimal substructure. Start with a plain recursive solution, add memoisation (cache results by their arguments), then, if needed, convert it to a bottom-up table and reduce the memory to only the rows you need.
Most DP problems fit a few families: one-dimensional sequences (climbing stairs, house robber), two-dimensional grids (unique paths, minimum path sum), two strings (longest common subsequence, edit distance), knapsack-style choices (coin change, partition equal subset sum), and intervals. The hard part is defining the state in words, for example 'dp[i] is the fewest coins to make amount i', then writing the transition. Say the state definition out loud in interviews; it shows the interviewer you understand the problem.
Practice. Solve coin change, longest increasing subsequence and edit distance, each first with memoisation and then bottom-up.
Stage 4 Coding interview technique
Goal. Recognise common problem patterns and solve medium-difficulty problems in 25–35 minutes while explaining your thinking clearly.
A repeatable problem-solving framework Core
Use the same steps every time so nerves do not decide your approach:
- Clarify: restate the problem, ask about input size, duplicates, negative numbers, empty input and expected output format.
- Examples: work one normal and one edge example by hand.
- Brute force: state the obvious solution and its complexity in one or two sentences.
- Optimise: look for the bottleneck and a pattern that removes it; agree the plan with the interviewer before coding.
- Code: write clean code with good names and helper functions.
- Test: trace your example through the code, then check edge cases, fix bugs calmly.
- Analyse: state time and space complexity and possible improvements.
Practice. Record yourself solving one medium problem out loud using all seven steps and review where you skipped or rushed one.
Two pointers and sliding window Core
Two pointers move through a sorted array or a string from both ends or at different speeds, replacing a nested loop with one pass: pair sums in a sorted array, removing duplicates in place, container with most water, three-sum after sorting.
A sliding window keeps a contiguous range and moves its edges: extend the right edge, and shrink the left edge while the window breaks a rule. It solves 'longest substring with at most k distinct characters', 'minimum window containing all letters' and fixed-size window averages in O(n).
def longest_unique(s):
seen, left, best = {}, 0, 0
for right, ch in enumerate(s):
if ch in seen and seen[ch] >= left:
left = seen[ch] + 1
seen[ch] = right
best = max(best, right - left + 1)
return best
Practice. Solve three-sum, longest substring without repeating characters and minimum window substring.
Pattern recognition across problem types Core
Interviewers recycle a limited set of ideas. Learn the cue for each:
- Sorted input or 'minimum value that works': binary search.
- 'Top k', 'closest k', merge sorted streams: heap.
- Shortest steps in a grid or unweighted graph: BFS.
- Dependencies or ordering: topological sort.
- All combinations or arrangements: backtracking.
- Count ways or best value with choices at each step: dynamic programming.
- Overlapping ranges or meetings: sort by start, then merge or sweep.
- Next greater or smaller element: monotonic stack.
- Connected groups that merge over time: union-find.
- Seen-before, counts, complements: hash map.
After each problem, write a one-line note of the cue that should have led you to the pattern.
Practice. Keep a pattern log; for every problem you solve, record the pattern, the cue and the mistake you made, and review it weekly.
Communicating during the coding round Core
Interviewers score communication as much as the final code. Think out loud: say what you are considering and why you reject it. Ask clarifying questions before coding, not after. When stuck, say what you know and where the difficulty is; interviewers often give a hint, and taking hints well is a positive signal.
Write code a colleague could review: meaningful names, small helpers, no clever tricks you cannot explain. If you notice a bug, say so and fix it rather than hoping it goes unnoticed. Manage time: if the optimal solution is not coming after about ten minutes, code a correct simpler one, then improve it. A working O(n log n) answer beats an unfinished O(n) one.
Practice. Do three mock interviews with a friend or peer where they play interviewer, and ask them to score clarity, not only correctness.
Practice strategy and spaced repetition Core
Volume without review does not stick. A practical plan: about 150 to 200 well-chosen problems spread across all patterns, mostly medium difficulty, with a smaller number of easy warm-ups and hard stretch problems. Time-box each attempt to 30–40 minutes; if you fail, study the solution, then re-solve it from scratch two days and one week later.
Track problems in a list with the date, pattern, result and notes. Mix topics once you have covered each pattern, because real interviews do not tell you the category. Practise in a plain editor without autocomplete at least some of the time, and do timed sessions that match the real round length. Use coding practice for timed problems and progress page to track weak areas.
Practice. Build a spreadsheet of 40 problems across ten patterns and complete it over three weeks, re-solving every failed one twice.
Online assessments and take-home tasks Recommended
Many companies start with an automated online assessment: two to four problems in 60–90 minutes, graded by hidden test cases. Read every constraint, handle edge cases and large inputs, and make sure your solution is efficient enough not to time out. Submit partial solutions that pass some tests rather than nothing.
Take-home tasks ask you to build a small application or feature over a few days. Graders look at structure, tests, a clear README with setup steps and trade-offs, and sensible scope. Do not over-engineer; a small, well-tested and clearly explained solution scores better than an ambitious unfinished one. Expect a follow-up call where you extend or defend your code, so make sure you understand every line, including any written with AI help.
Practice. Build a small command-line expense tracker as if it were a take-home: tests, README and a section explaining what you would do with more time.
Stage 5 Professional engineering practice
Goal. Work like a member of a real team: version control, code review, testing, delivery pipelines, readable code and production debugging.
Git and collaborative workflows Core
Git tracks changes to code. Learn commits, branches, merging, rebasing, resolving conflicts, and inspecting history with log, diff and blame. Write commit messages that explain why, not only what. Keep commits small and focused so they are easy to review and revert.
Most teams use a pull request workflow: create a short-lived branch, push it, open a pull request, get review and automated checks, then merge to the main branch. Trunk-based development keeps branches very short and uses feature flags to hide unfinished work. Know how to undo safely: git revert for shared history, git reset only for your own local work. The Git and GitHub Roadmap covers branching strategies and recovery in depth.
Practice. Simulate a merge conflict between two branches, resolve it, then use interactive rebase to tidy three messy commits into one clear one.
Code review Core
Code review catches bugs, spreads knowledge and keeps a codebase consistent. As an author, keep changes small, describe what and why in the pull request, point out risky parts and test what you changed. As a reviewer, check correctness first (edge cases, error handling, concurrency, security), then design and readability, then style that tools cannot catch.
Be specific and kind: ask questions rather than issue orders, separate blocking issues from optional suggestions, and approve once concerns are addressed rather than demanding perfection. Review promptly, because waiting reviews slow the whole team. Senior candidates are often asked how they give difficult feedback or how they handle a reviewer they disagree with. The Code Review Roadmap goes deeper on checklists and review culture.
Practice. Review an open pull request in a public project and write three comments: one bug risk, one readability point and one question.
Testing strategy Core
Use a mix of tests. Unit tests check one function or class in isolation and run in milliseconds. Integration tests check that components work together, for example your code against a real database in a container. End-to-end tests drive the whole system like a user; they are slow and brittle, so keep them few. This balance is often drawn as a test pyramid.
Use test doubles (mocks, stubs, fakes) to isolate code from slow or unreliable dependencies, but do not mock so much that tests only check your mocks. Aim for tests that fail for one clear reason, run fast and deterministically, and cover behaviour that matters rather than chasing a coverage percentage. Add a regression test for every bug you fix.
Practice. Add unit tests and one integration test against a real PostgreSQL container to a small API you have written.
CI/CD and deployment Core
Continuous integration runs builds, linters and tests automatically on every push, so problems surface within minutes. Continuous delivery keeps the main branch always deployable and automates releases through environments such as staging and production.
Learn the deployment strategies: rolling updates replace instances gradually, blue-green switches traffic between two full environments, and canary releases send a small share of traffic to the new version first and watch error rates. Feature flags separate deploying code from releasing a feature. Know how to roll back quickly. You will meet tools such as GitHub Actions, GitLab CI or Jenkins, and containers built with Docker; the Docker Roadmap explains containers in detail.
Practice. Set up a pipeline for a small project that runs linting and tests on every pull request and builds a Docker image on merge.
Clean code and refactoring Core
Readable code is cheaper to change. Use names that say what something is or does, keep functions short and at one level of abstraction, avoid deep nesting with early returns, and remove duplication once the pattern is clear rather than guessing at abstractions too early. Comments should explain why, not repeat what the code says.
Refactoring changes structure without changing behaviour, in small steps protected by tests: extract function, rename, move method, replace conditionals with polymorphism, introduce a parameter object. Learn to recognise code smells such as long methods, large classes, feature envy and primitive obsession.
Practice. Take a 200-line function from an old project and refactor it into small, tested pieces without changing its output.
Observability and production debugging Core
Once code runs in production you need to see what it does. The three main signals are logs (structured events with context such as request ID and user ID), metrics (counters and histograms such as request rate, error rate and latency percentiles) and traces (the path of one request across services). Alerts should fire on symptoms users feel, such as error rate or p99 latency, not on every internal blip.
When something breaks, stabilise first: roll back or disable the feature, then investigate. Check what changed recently, compare dashboards before and after, and narrow down by service, endpoint and host. Afterwards, write a blameless post-incident review with a timeline, root cause and follow-up actions.
Practice. Add structured logging, a request-latency metric and a health endpoint to a small service, then simulate a slow dependency and find it from the data.
Documentation and design docs Recommended
Engineers write constantly: READMEs, API references, runbooks and design documents. A good design doc states the problem and context, goals and non-goals, the proposed design, alternatives considered with their trade-offs, risks, rollout plan and open questions. Writing it before coding exposes disagreements early, when they are cheap to resolve.
Keep documents short and specific, with diagrams where they help, and update them when decisions change. Strong written communication is one of the clearest signals of seniority, and staff-level interviews often ask you to walk through a design doc you wrote and how it was received.
Practice. Write a two-page design doc for adding a 'forgot password' flow to an app, including two alternatives you rejected and why.
Stage 6 Building real systems
Goal. Build and deploy a working backend service with a database, a well-designed API, authentication, caching and tests.
SQL and data modelling Core
Most applications store their core data in a relational database such as PostgreSQL or MySQL. Model entities and relationships first: one-to-many with a foreign key, many-to-many with a join table. Choose primary keys, add unique constraints for business rules (one account per email) and NOT NULL where data is required, so the database guards correctness.
Write queries with joins, aggregation and window functions, read query plans with EXPLAIN, and add indexes on columns used in filters, joins and sorting. Know the classic performance traps: N+1 queries from an ORM, missing indexes, selecting far more columns than needed, and long transactions holding locks. Understand transaction isolation levels at a basic level. The SQL Roadmap covers queries and modelling in depth.
Practice. Design a schema for an online shop, load sample data and write queries for revenue by month and each customer's latest order, checking each plan with EXPLAIN.
API design Core
APIs are the contracts between services and clients. REST models resources with URLs and HTTP methods: GET /orders/42, POST /orders. Use proper status codes, consistent naming, pagination for lists (cursor-based for large or changing data), filtering, and versioning for breaking changes. Make write operations safe to retry with idempotency keys.
gRPC uses Protocol Buffers and HTTP/2 for fast, typed service-to-service calls. GraphQL lets each client ask for precisely the fields it uses, which suits varied front ends but needs care with query cost and caching. Design for the client: clear error formats, sensible defaults and documentation, often with an OpenAPI specification. The API Design Roadmap covers these choices in detail.
Practice. Design and document a REST API for a to-do app with pagination, filtering, idempotent creation and consistent error responses.
Authentication, authorisation and security basics Core
Authentication proves who a user is; authorisation decides what they may do. Store passwords only as slow, salted hashes using bcrypt, scrypt or Argon2, never plain or with a fast hash. Sessions keep state on the server with a cookie ID; tokens such as JWTs carry signed claims and must be short-lived and validated properly. OAuth 2.0 and OpenID Connect handle 'log in with another provider' and delegated access.
Know the common vulnerabilities and their fixes: SQL injection (parameterised queries), cross-site scripting (escape output), cross-site request forgery (same-site cookies and tokens), broken access control (check ownership on every request), and secrets in code (use a secrets manager). Validate all input at the boundary and apply least privilege everywhere.
Practice. Add sign-up and login with hashed passwords to your API, then write a test that proves a user cannot read another user's data.
Caching Core
A cache stores the result of expensive work so the next request is fast. Caches exist at every level: browser, CDN, application memory and shared caches such as Redis. The common pattern is cache-aside: read from the cache, and on a miss load from the database and store the result with an expiry time.
The hard part is staleness. Choose a time-to-live that matches how fresh the data must be, invalidate or update entries on writes, and decide what happens when the cache is down. Watch for the thundering herd, when a popular key expires and many requests hit the database at once; use request coalescing or slightly randomised expiry. Eviction policies such as LRU decide what to drop when memory fills. The Redis Roadmap explains a popular cache in depth.
Practice. Add Redis cache-aside to a slow endpoint, measure latency before and after, and write the invalidation for updates.
Concurrency and asynchronous work in services Recommended
Real services handle many requests at once. Know your runtime's model: a thread per request (classic Java), an event loop with async I/O (Node.js, Python asyncio), or lightweight goroutines in Go. Avoid blocking calls on an event loop, and keep shared mutable state to a minimum.
Move slow work out of the request path with a background job queue: the API records the request, a worker sends the email or resizes the image later. Design jobs to be idempotent, since they may run twice, and handle retries with backoff and a dead-letter queue for failures. Use database transactions or optimistic locking with a version column to stop two requests overwriting each other's changes.
Practice. Make an image-upload endpoint return immediately and process thumbnails in a background worker with retries.
Cloud, containers and infrastructure basics Recommended
Most software runs in the cloud. Learn the building blocks offered by AWS, Azure or Google Cloud: virtual machines, managed databases, object storage, load balancers, queues and serverless functions. Containers package an app with its dependencies so it runs the same everywhere; Kubernetes schedules and scales containers across many machines.
You do not need to be an operations specialist, but you should be able to deploy your own service, read its logs, set environment configuration, and understand costs and basic network security such as private subnets and security groups. Infrastructure as code tools such as Terraform describe these resources in version-controlled files. The Cloud Computing Roadmap and Kubernetes Roadmap go further.
Practice. Deploy your API and its database to a cloud provider with a container, a managed database and a load balancer, and tear it down afterwards to avoid costs.
Stage 7 Low-level design and object-oriented design
Goal. Turn a problem statement into well-structured classes and interfaces, apply design principles and patterns, and pass a low-level design round.
SOLID and core design principles Core
SOLID is five principles for code that stays easy to change:
- Single responsibility: a class has one reason to change.
- Open/closed: add behaviour by adding code, not by editing stable code.
- Liskov substitution: a subtype must work wherever its parent type is expected.
- Interface segregation: prefer small focused interfaces over large ones.
- Dependency inversion: depend on abstractions, and inject concrete implementations.
Pair them with DRY (do not repeat yourself), KISS (keep it simple), YAGNI (do not build what you do not need yet), high cohesion and low coupling. Apply them with judgement: an interface with one implementation and no test need is just noise.
Practice. Take a class that formats, validates and saves reports, and split it into SOLID components with tests for each.
Design patterns Core
Design patterns are named solutions to recurring design problems. Learn the ones that appear most often:
- Creational: factory and builder for constructing objects, singleton (use sparingly, it is global state).
- Structural: adapter to fit an existing class to a new interface, decorator to add behaviour by wrapping, facade to simplify a subsystem.
- Behavioural: strategy for swappable algorithms, observer for publish and subscribe, command for queued or undoable actions, state for objects whose behaviour depends on their state.
In interviews, name a pattern only when it solves a problem you can point to, such as 'pricing rules vary by vehicle type, so I use a strategy'. The Software Design and Architecture Roadmap covers patterns and principles in depth.
Practice. Implement a payment module where card, wallet and bank transfer are strategies, and add logging with a decorator.
The low-level design interview Core
In a low-level design (LLD) or object-oriented design round you design classes for a contained problem in 45–60 minutes: a parking lot, an elevator system, a library, a vending machine, a chess game, a rate limiter, a splitwise-style expense sharer. Some companies want runnable code; others want class diagrams and key methods.
A good approach: clarify requirements and scope, list the core entities (nouns) and actions (verbs), define classes, their fields and relationships, then the interfaces between them. Walk through one or two use cases to prove the design works. Discuss extensibility (adding a new vehicle type), concurrency (two cars taking the same spot) and how you would test it. Keep it simple first, then extend.
Practice. Design a parking lot with multiple floors, spot sizes and payment, write the core classes in your language and walk through parking and leaving.
Concurrency in object design Recommended
LLD problems often hide a concurrency question: two users booking the last seat, two elevators answering the same call, many threads using a rate limiter. Identify the shared state and decide how to protect it: a lock around the critical section, finer-grained locks per resource, atomic compare-and-set, or a single-threaded owner that processes commands from a queue.
Know the standard tools in your language, such as synchronised blocks, ReentrantLock and concurrent maps in Java, mutexes and channels in Go, and threading.Lock in Python. Be ready to implement a thread-safe singleton, a bounded blocking queue (producer-consumer) and a thread-safe LRU cache, and to explain how you would test them.
Practice. Implement a bounded blocking queue with one producer and two consumers, and prove with a test that no item is lost or duplicated.
Layered and clean architecture Recommended
Larger codebases need structure beyond individual classes. A layered architecture separates presentation, business logic and data access, with dependencies pointing downwards. Hexagonal or clean architecture goes further: business logic sits in the centre with no knowledge of frameworks or databases, and adapters at the edges connect it to HTTP, databases and queues through interfaces.
The payoff is testability and the ability to swap infrastructure without rewriting rules. The cost is more files and indirection, which is not worth it for a small script. Know the terms because they come up in senior interviews and code reviews, and be able to judge when a simpler structure is the better choice.
Practice. Restructure a small service so its business logic has no imports from the web framework or database library, and test it with in-memory fakes.
Stage 8 System design
Goal. Design a scalable, reliable service end to end in a 45–60 minute interview, explaining each trade-off.
The system design interview approach Core
System design rounds are open-ended: 'design a URL shortener', 'design a chat app'. Use a clear structure:
- Requirements: functional (what users do) and non-functional (scale, latency, availability, consistency). Agree scope.
- Estimates: users, requests per second, storage, read-to-write ratio.
- API and data model: the main endpoints and tables or collections.
- High-level design: clients, load balancer, services, databases, caches, queues.
- Deep dives: the two or three hardest parts, chosen with the interviewer.
- Wrap-up: bottlenecks, failure modes, monitoring and what you would improve.
Drive the conversation, state assumptions and justify each choice with a trade-off. Use system design practice to rehearse.
Practice. Time yourself designing a pastebin in 45 minutes on paper using all six steps, then review which step ran over.
Back-of-the-envelope estimation Core
Estimates tell you whether one machine is enough or you need hundreds. Know round numbers: a day has about 86,400 seconds, so 1 million requests a day is roughly 12 per second on average, with peaks several times higher. Memory access takes nanoseconds, an SSD read around a hundred microseconds, and a cross-region round trip tens to a hundred-plus milliseconds.
Estimate storage as items per day times size times retention; for example 10 million new 1 KB records a day is about 10 GB a day, or under 4 TB a year. Estimate read and write throughput separately, because a read-heavy system calls for caches and replicas while a write-heavy one calls for partitioning and append-friendly storage. Round aggressively and say your assumptions aloud.
Practice. Estimate requests per second, storage per year and cache size for a photo-sharing app with 50 million daily users.
Scaling: load balancing and stateless services Core
Vertical scaling means a bigger machine; horizontal scaling means more machines. Horizontal scaling needs stateless application servers, with session data moved to a shared store, so any server can handle any request. A load balancer spreads traffic using round robin, least connections or consistent hashing, runs health checks and removes unhealthy servers.
Layer 4 balancers route by IP and port; layer 7 balancers understand HTTP and can route by path or header. Put a CDN in front for static files and cacheable responses close to users. Add autoscaling based on CPU or queue length. A reverse proxy or API gateway can terminate TLS, apply rate limits and authentication, and route to services.
Practice. Draw a design that takes a single-server web app to three regions, labelling where state lives at each step.
Databases at scale: replication, partitioning and indexing Core
Replication copies data to several nodes. Leader-follower replication sends writes to one leader and spreads reads across followers, which may lag, so a user might not see their own write; route such reads to the leader. Multi-leader and leaderless designs allow writes in several places but must resolve conflicts.
Partitioning (sharding) splits data across nodes by a key. Hash partitioning spreads load evenly; range partitioning keeps ranges together for scans but can create hot spots. Choose a key that matches the main access pattern and avoids celebrities or hot keys. Cross-shard queries and transactions become expensive, so design to avoid them. Know when a relational database, a document store, a wide-column store or a search engine is the right fit, and why.
Practice. For a messaging app, choose a shard key for the messages table, and describe how a user's inbox query and a new-message write each work.
Consistency, availability and the CAP theorem Core
The CAP theorem says that when a network partition splits a distributed system, it must choose between consistency (every read sees the latest write) and availability (every request gets a response). In practice the choice is per feature: a bank balance needs strong consistency; a like count can be eventually consistent.
Know the spectrum: strong consistency, read-your-writes, monotonic reads and eventual consistency. Quorum reads and writes (W + R > N) give stronger guarantees in leaderless stores. Distributed transactions across services are hard; the saga pattern breaks them into local steps with compensating actions, and the outbox pattern publishes events reliably alongside a database write. Consensus algorithms such as Raft keep replicated state agreed and underpin leader election.
Practice. For an online store, list five pieces of data and decide for each whether it needs strong or eventual consistency, with one sentence of justification.
Message queues and event-driven design Core
Queues decouple producers from consumers, absorb traffic spikes and let slow work happen asynchronously. A work queue such as RabbitMQ or SQS delivers each message to one worker. A log-based stream such as Kafka keeps an ordered, replayable log split into partitions, and many consumer groups can read it independently.
Understand delivery guarantees: at-most-once may lose messages, at-least-once may duplicate them, so consumers must be idempotent; effectively-once combines at-least-once with deduplication. Ordering is usually guaranteed only within a partition, so choose the partition key carefully (for example by user ID). Monitor consumer lag and use dead-letter queues for messages that keep failing. Event-driven designs make services independent but harder to trace, so invest in correlation IDs and tracing.
Practice. Design order processing where payment, inventory and email services react to an OrderPlaced event, and explain how you prevent double charging.
Reliability, rate limiting and resilience Core
Systems fail; design so failures stay small. Use timeouts on every network call, retries with exponential backoff and jitter for transient errors, and circuit breakers that stop calling a failing dependency for a while. Bulkheads isolate resources so one slow feature cannot exhaust every thread. Degrade gracefully: show cached or partial results rather than an error page.
Rate limiting protects services from overload and abuse. Token bucket allows bursts up to a limit with a steady refill rate; sliding window counters give smoother limits. In a distributed system, store counters in a shared store such as Redis or accept approximate per-node limits. Define availability targets as SLOs (for example 99.9% of requests succeed within 300 ms) and plan redundancy across zones or regions to meet them.
Practice. Implement a token bucket rate limiter, then sketch how to make it work across ten API servers.
Classic system design problems Core
Practise the problems that come up repeatedly, each of which teaches a reusable idea:
- URL shortener: ID generation, base-62 encoding, read-heavy caching.
- News feed: fan-out on write versus fan-out on read, and a hybrid for very popular accounts.
- Chat system: WebSockets, presence, message ordering and delivery receipts.
- Rate limiter and distributed cache: shared counters, consistent hashing and eviction.
- Notification service: queues, retries, user preferences and provider failover.
- File storage or video streaming: object storage, chunked uploads, CDN delivery.
- Search autocomplete, ride matching and ticket booking: tries, geospatial indexes and preventing double booking.
The System Design Roadmap covers each building block in depth.
Practice. Design one classic problem per week for eight weeks, write a one-page summary of each, and explain it aloud to a peer.
Stage 9 Behavioural interviews, levelling and the job search
Goal. Present your experience clearly, pass behavioural rounds at your target level and run an organised job search through to an offer.
Behavioural interviews and the STAR method Core
Behavioural rounds check how you work with people and handle difficulty. Answer with STAR: Situation (brief context), Task (your responsibility), Action (what you did, in the first person, with detail) and Result (the outcome, measured where possible, and what you learned). Spend most of the time on actions.
Prepare eight to ten stories that you can adapt: a project you are proud of, a technical challenge, a conflict with a colleague, a mistake or failure, a time you missed a deadline, giving and receiving feedback, helping someone grow, influencing without authority, and a time you changed your mind. Be honest about failures and show what you changed afterwards. Many companies publish their values; map your stories to them.
Practice. Write ten STAR stories in bullet form, then tell each aloud in under three minutes and cut anything that is not your own action.
Levels: new grad to staff Core
Interviews are calibrated to a level, and the bar changes with it. A new graduate or junior engineer is judged mainly on coding fundamentals and learning ability. A mid-level engineer is expected to own features end to end with little guidance. A senior engineer leads projects, makes design decisions across a system, mentors others and handles ambiguity; system design and behavioural rounds weigh heavily. A staff engineer sets technical direction across teams, resolves cross-team problems and multiplies others' output.
Know which level you are targeting and tell stories with matching scope: a senior candidate's stories should involve leading others and making trade-offs, not only writing code.
Practice. Write down the scope of your three biggest pieces of work (people involved, systems touched, decisions you made) and compare it with the level descriptions on this page.
CV, portfolio and online presence Core
A software engineering CV should be one page for early careers and at most two for experienced engineers. List experience with bullets that show impact: what you built, the technologies, and a result such as reduced latency, saved hours or users served. Put skills in a short section; do not rate yourself with stars or percentages.
For students and career changers, two or three solid projects matter more than ten tutorial clones. Each should have a README explaining the problem, design, how to run it and tests. Deployed, working projects stand out. Contributions to open source show you can work in someone else's codebase.
Practice. Rewrite three CV bullets using the pattern 'did X using Y, resulting in Z', with a number in each where honest.
Running the job search Core
Treat the search as a project. Build a target list of companies across tiers, track each application with its stage and next step, and schedule interviews so the ones you care most about come after a few practice runs. Referrals from people who know your work significantly improve response rates, so ask former colleagues and classmates directly and specifically.
Learn each company's loop before you start: number of rounds, languages allowed, whether LLD or system design is included, and the level. Recruiters usually tell you if you ask. After each interview, write down the questions and how you did, and adjust your practice.
Practice. Build a tracker with 20 target companies, their interview format and a referral contact or plan for each.
Working effectively with AI coding tools Recommended
AI assistants now write, explain and review a large share of everyday code. Used well, they speed up boilerplate, tests, unfamiliar APIs and refactors. Used badly, they produce plausible code that is subtly wrong, insecure or inconsistent with the codebase. The skill that matters is judgement: specify the task precisely, review every line as you would a colleague's pull request, run the tests, and understand the result well enough to defend it.
Interview formats are adapting: some ban assistants, some allow them and focus on how you guide, check and correct the output. Fundamentals matter more, not less, because you must spot when the tool is wrong. Check each company's policy before an interview and follow it. The AI-Assisted Coding Roadmap covers effective workflows.
Practice. Use an AI assistant to add a feature to one of your projects, then list every change you had to correct and why.
Projects to build
Build these in order. Each one should end up in a public repository with a short README that explains your decisions.
- Command-line task managerBeginner
Build a command-line tool to add, list, complete and search tasks, saving them to a JSON file or SQLite. Add unit tests, input validation and a clear README. Focus on clean functions and good error messages.
Core language features · File and database I/O · Unit testing · Git
- REST API with authenticationIntermediate
Build a bookmarks or notes service with users, login with hashed passwords, CRUD endpoints, pagination and per-user access control, backed by PostgreSQL. Add integration tests, a CI pipeline and deploy it to a cloud provider in a container.
API design · SQL and data modelling · Authentication · CI/CD · Docker
- URL shortener at scaleIntermediate
Build a URL shortener with base-62 IDs, Redis caching, click analytics written through a queue to a separate store, and rate limiting. Load-test it, record latency percentiles and write a design doc explaining how it would scale to many millions of links.
System design · Caching · Message queues · Load testing · Design docs
- Real-time chat serviceAdvanced
Build a chat backend with WebSockets, rooms, message history, presence and delivery receipts. Run two server instances behind a load balancer and use a pub/sub channel so users on different servers can talk. Handle reconnects and duplicate messages.
Concurrency · WebSockets · Distributed state · Idempotency · Observability
- Low-level design portfolioAdvanced
Implement five classic LLD problems (parking lot, elevator, library, vending machine and a thread-safe LRU cache) in runnable code with tests, class diagrams and notes on the patterns and trade-offs used.
Object-oriented design · Design patterns · Concurrency · UML
Preparation plan
- Weeks 1–2Foundations refresh and core data structures
Review Big-O, arrays, strings, hash maps, stacks and queues; solve two to three easy and medium problems a day in coding practice.
Start your pattern log and an application tracker with target companies and their interview formats.
Draft your CV and list candidate STAR stories from your experience.
- Weeks 3–4Trees, graphs, heaps and binary search
Cover tree traversals, BST problems, BFS and DFS on grids and graphs, topological sort, heaps and binary search on the answer.
Re-solve every failed problem from weeks 1–2 without notes.
Ask for referrals at your first-tier companies so screens start around week 6.
- Weeks 5–6Backtracking, dynamic programming and first mocks
Work through the main DP families and backtracking templates, writing the state definition before coding.
Do two timed mock coding interviews with a peer and note communication gaps.
Begin low-level design: SOLID, five key patterns and the parking lot and LRU cache problems in code.
- Weeks 7–8System design building blocks
Study load balancing, caching, replication, sharding, queues, consistency and rate limiting; practise estimation daily for ten minutes.
Design a URL shortener and a rate limiter end to end in system design practice, out loud and timed.
Keep solving mixed medium problems, four or five a week, to stay sharp.
- Weeks 9–10Classic designs and behavioural stories
Design a news feed, a chat system and a notification service; write a one-page summary of each.
Finalise ten STAR stories and a project deep dive; rehearse each aloud and time them.
Take interviews at practice-tier companies and adjust your plan from the feedback.
- Weeks 11–12Full mock loops and target interviews
Run two full mock loops (coding, design and behavioural back to back) to build stamina.
Revisit weak patterns from your log and the progress page view; re-solve your hardest twenty problems.
Interview at your top companies, write notes after each round, and prepare for negotiation.
Interview questions
Try to answer each question out loud before you open the answer.
Return the positions of two numbers in an integer array that sum to a given target.Junior
The brute force checks every pair in O(n²). Better: walk the array once with a hash map from value to index. For each number, compute target - x; if it is already in the map, return both indices, otherwise store x. That is O(n) time and O(n) space. I would ask whether the array can contain duplicates, whether exactly one answer is guaranteed and what to return if none exists. If the array were sorted, two pointers from both ends would give O(1) extra space. I test with a pair at the start, at the end, and with duplicate values such as [3, 3] and target 6.
What is the time complexity of hash map operations, and when can it get worse?Junior
Insert, lookup and delete are O(1) on average. The key is hashed to a bucket, and with a good hash function and a sensible load factor each bucket holds few items. It degrades towards O(n) when many keys collide into the same bucket, because of a poor hash function, deliberately crafted keys or a table that is too full. Resizing costs O(n) occasionally, but it is amortised over many inserts so appends stay O(1) amortised. Some implementations, such as Java's HashMap, switch long collision chains to balanced trees to cap the worst case at O(log n).
Find the length of the longest substring without repeating characters.Mid
Sliding window. Keep a left pointer and a map from character to its last index. Move the right pointer through the string; if the current character was seen at an index at or after left, move left to just past that index. Update the map and track the best window length. Each character is processed once, so time is O(n), and space is O(min(n, alphabet size)). A common bug is moving left backwards when the previous occurrence is before the window; the check against left prevents it. Test with an empty string, all identical characters and 'abba'.
You have courses with prerequisites. Can a student finish all of them, and in what order?Mid
Model courses as nodes and prerequisites as directed edges; the question is whether the graph has a cycle and, if not, a topological order. Kahn's algorithm: compute in-degrees, put all nodes with in-degree zero in a queue, repeatedly pop one, add it to the order and decrease its neighbours' in-degrees, enqueuing any that reach zero. If the order contains every course, it is valid; otherwise a cycle exists. Time and space are O(V + E). A DFS with three colours (unvisited, visiting, done) also detects cycles and produces a reverse post-order.
How do you approach a dynamic programming problem you have not seen before?Mid
First I check the signals: counting ways or finding a best value where each step involves a choice, and where the brute-force recursion would recompute the same subproblems. Then I define the state in words, for example 'dp[i] is the minimum cost to reach step i', write the transition from smaller states and the base cases, and implement it top-down with memoisation because that follows the recursion naturally. Once correct, I consider converting it to a bottom-up table and reducing memory to the last row or two. I state complexity as the number of states times the work per state.
Design an LRU cache with O(1) get and put.Mid
Combine a hash map from key to node with a doubly linked list ordered by recent use. On get, look up the node, move it to the front and return its value. On put, update and move an existing node, or insert a new node at the front; if the size exceeds capacity, remove the node at the tail and delete its key from the map. Dummy head and tail nodes remove edge cases. Both operations are O(1). For a multi-threaded version, the simplest correct answer is one lock around both operations; for higher throughput, shard the cache into segments, each with its own lock.
Design a parking lot system.Mid
I clarify: multiple floors, spot sizes (motorcycle, car, large), entry and exit gates, tickets and payment. Core classes: ParkingLot holding Floors, each with Spots of a SpotType; Vehicle with a size; Ticket with entry time and spot; and a PricingStrategy interface so hourly or flat pricing can change without editing other code. A SpotAllocator finds the nearest free compatible spot, keeping free spots per type in a structure such as a min-heap by distance. Two cars must not get the same spot, so allocation is atomic, using a lock per spot type. I walk through entry and exit to show the design works.
Design a URL shortener.Mid
Requirements: create a short link, redirect quickly, optional expiry and analytics; reads far outnumber writes. Generate IDs from a counter or ID service and encode them in base 62; seven characters give trillions of codes. Store the mapping in a key-value or relational store keyed by code. Redirects hit a cache such as Redis first, since popular links dominate, and return 301 or 302 depending on whether we need to count every click. Click events go to a queue and are aggregated separately so redirects stay fast. Scale with stateless app servers behind a load balancer, read replicas or a partitioned store, and rate limits on creation to stop abuse.
Design a rate limiter for a public API.Senior
Clarify limits per user, API key or IP, the precision needed and whether limits are global across servers. A token bucket per client allows short bursts with a steady refill rate; a sliding window counter gives smoother limits with little memory. For many API servers, store counters in Redis and update them atomically with a script, keyed by client and window. Put the limiter in the API gateway so every service benefits. Return 429 with a Retry-After header. Decide on failure behaviour: if Redis is down, fail open with local approximate limits rather than blocking all traffic. Monitor rejected requests per client.
Design a news feed for a social network.Senior
Users post and see a ranked feed of people they follow. The key decision is fan-out. Fan-out on write pushes each new post ID into every follower's precomputed feed cache, giving fast reads but expensive writes for accounts with millions of followers. Fan-out on read assembles the feed at request time, cheap to write but slow to read. A hybrid pushes posts from ordinary users and merges posts from very popular accounts at read time. Posts live in a partitioned store, feeds in a cache as lists of IDs, media on a CDN. Pagination uses cursors. Ranking runs as a separate service, and new-post events flow through a queue to the fan-out workers.
What happens when you type a URL into a browser and press enter?Junior
The browser parses the URL and checks its caches. It resolves the domain to an IP address through DNS, using caches at each level before asking resolvers and authoritative servers. It opens a TCP connection, or QUIC for HTTP/3, and performs a TLS handshake to agree keys and verify the certificate. It sends an HTTP request with headers and cookies. The request may pass a CDN, a load balancer and a reverse proxy before reaching an application server, which may query caches and databases and returns a response. The browser parses the HTML, fetches CSS, JavaScript and images, builds the page and renders it.
When would you choose a relational database over a NoSQL store?Mid
I default to a relational database such as PostgreSQL when data has clear relationships, needs transactions across several rows or tables, or must support flexible queries and reports, for example orders, payments and accounts. I consider a NoSQL store when the access pattern is simple and known, such as lookups by key, and the scale or write volume exceeds what one relational node handles comfortably, or when records are naturally self-contained documents. The real trade-off is query flexibility and transactional guarantees against horizontal scale and schema flexibility. Many systems use both, for example PostgreSQL for orders and a key-value store for sessions.
Latency on a service you own doubled after lunch. How do you investigate?Senior
First, assess impact and stabilise: check error rates and whether a recent deploy or config change lines up; if so, roll it back. Then narrow down with data: which endpoints, which hosts or zones, p50 or only p99? Look at traces for slow spans, for example a database query or downstream call. Check resource metrics such as CPU, memory, garbage collection pauses, connection pool saturation and queue depth, and the health of dependencies. Compare with traffic: a new client or a spike can explain it. Once the cause is found, fix it, confirm on the dashboards and write a blameless review with follow-ups such as a missing alert or load test.
Tell me about a time you disagreed with a teammate on a technical decision.Mid
A strong answer follows STAR and shows respect and data. For example: a colleague wanted to add a new message queue for a feature; I believed a database table polled by a worker was enough at our scale. I asked to understand their concerns first, then we wrote down the requirements and expected load, and I built a quick prototype to measure throughput. The numbers showed the simple design had ample headroom, and we agreed to revisit if load grew ten times. The result was a smaller change shipped a week earlier. The lesson I share: disagree on evidence, write the criteria down, and commit fully once a decision is made.
Tell me about a mistake you made in production and what you learned.Junior
Interviewers want honesty, ownership and learning. Choose a real mistake with a moderate impact. Describe the situation briefly, then what you did: noticed or were alerted, raised it immediately, helped roll back or fix, and kept people informed. Then the result, such as minutes of degraded service, and most importantly what changed afterwards: a test that would have caught it, a checklist, a safer deploy process or an alert. Avoid blaming others or choosing a trivial non-mistake. Ending with a concrete process change shows maturity at any level.
Describe a time you influenced technical direction beyond your own team.Senior
This is a staff-level signal. A good story: several teams each maintained their own retry and timeout logic and incidents kept cascading. I gathered incident data showing the pattern, wrote a short proposal for a shared client library with sensible defaults, and reviewed it with the leads of the affected teams, adjusting it to their constraints. I built the first version with one team, measured fewer cascading failures, then helped two more teams adopt it and added it to the service template. The result was fewer incidents and less duplicated code. The emphasis is on data, writing, consensus and following through on adoption, not on authority.
Common mistakes
- Grinding hundreds of problems without reviewing them; keep a pattern log and re-solve failed problems after two days and a week.
- Jumping into code before clarifying the problem; always restate it, ask about constraints and agree an approach first.
- Practising in silence and then freezing when asked to explain; rehearse out loud and do mock interviews with another person.
- Learning system design by memorising diagrams; learn the building blocks and practise justifying each trade-off for given requirements.
- Skipping behavioural preparation because it seems easy; write and rehearse STAR stories, since this round often decides the level.
- Building many tutorial clones instead of a few complete projects; finish two or three with tests, a README and a live deployment.
- Over-engineering low-level design answers with every pattern you know; start simple and add a pattern only when a requirement calls for it.
- Interviewing at the most important companies first; schedule practice interviews early so your best performance comes later.
Ready-for-interview checklist
- I can solve most medium problems in my main language within 35 minutes while explaining my approach.
- I can state time and space complexity for any solution I write and explain amortised cost.
- I can implement BFS, DFS, binary search, a heap-based top-k, topological sort and union-find from memory.
- I can recognise the common problem patterns from their cues and choose an approach quickly.
- I can solve dynamic programming problems by defining the state in words and the transition.
- I can design classes for a parking lot, elevator or booking system using SOLID and suitable patterns.
- I can lead a 45-minute system design discussion from requirements and estimates to deep dives.
- I can explain caching, replication, sharding, queues, consistency models and rate limiting with trade-offs.
- I can build, test and deploy a backend service with a database, authentication and CI.
- I have eight to ten STAR stories with scope that matches my target level.
- I can walk through one past project in depth, including its design, trade-offs and what I would change.
Where this role leads
Specialise as a Back End Engineer, Front End Engineer or Full Stack Developer once you know which part of the stack you enjoy most.
Move into infrastructure and reliability as a DevOps SRE Engineer or Cloud Engineer.
Add machine learning skills to become a Machine Learning Engineer or AI Engineer.
Grow along the technical track to staff or principal engineer, or into a Software Architect role.
Move into people leadership as an Engineering Manager, keeping technical depth while growing a team.
Related roadmaps
- Data Structures and Algorithms RoadmapFrom arrays to graphs and DP, organised by interview pattern.
- System Design RoadmapScalability, caching, queues, consistency and the classic designs.
- Computer Science RoadmapTheory every engineer leans on: complexity, memory, OS, networks, compilers.
- Software Design and Architecture RoadmapClean code, SOLID, design patterns, DDD and architecture styles.
- Git and GitHub RoadmapCommits, branches, rebasing, pull requests, Actions and recovery.
- Python RoadmapSyntax to packaging: data model, typing, async, testing.
FAQ
How long does it take to become a software engineer from scratch?
With steady study of around 12 hours a week, most people need nine to fifteen months to reach a hireable junior level: several months on programming and fundamentals, a few on data structures and algorithms, and the rest on projects and interview practice. Full-time study shortens this. A computer science degree covers much of the theory but still needs dedicated interview practice. Experienced engineers preparing for a new job typically need ten to twelve weeks of focused preparation.
Which programming language should I use for coding interviews?
Use the language you know best. Python is the most popular choice because it is concise and has handy built-in collections, so you spend less time on syntax. Java, C++, C#, Go and JavaScript are all widely accepted. Avoid switching languages shortly before interviews. Check whether a company restricts languages for specific roles, such as C++ for some systems positions.
Is a computer science degree required to become a software engineer?
No. Many engineers are self-taught or come from bootcamps or other fields. Employers mainly check whether you can pass the interview and do the job. Without a degree, fill the fundamentals gap deliberately (complexity, data structures, operating systems, networking), and show evidence of skill through solid projects, open-source work or relevant experience. Some employers and visa processes do still ask for a degree, so check for the roles you want.
Are coding interviews still relevant now that AI writes code?
Most companies still run coding rounds, though formats are changing: some allow AI assistants and assess how you direct and check them, others use more practical or debugging tasks. The underlying skills of breaking down problems, choosing data structures, judging correctness and explaining trade-offs matter even more when you review generated code. Prepare for the classic format and check each company's policy.