Junior Software Engineer Interview Prep: Questions, Salary & Demand
Reviewed by Mark Dickie · Last updated
A Junior Software Engineer interview tests whether you can write working code, reason about data, and communicate clearly about how software systems fit together — across languages and layers of the stack. To prepare well, focus on Python fundamentals and SQL query writing first, since those come up in almost every early-career technical screen. From there, work outward to HTTP and API concepts, basic Node.js patterns, and the entry-level system design questions that ask you to sketch a simple service rather than architect a distributed system at scale. AI Engineering is an increasingly common addition to junior screens, so a working grasp of how models are called via APIs and where they fit in an application is worth your time.
What the interview covers
The questions in this guide draw from six technology areas. Here is what each one means at the junior level:
| Area | What you are expected to know | |---|---| | Python | Variables, data types, control flow, functions, list comprehensions, and basic OOP | | Databases & SQL | SELECT queries, filtering, joins, grouping, and understanding primary vs foreign keys | | HTTP & APIs | Request/response cycle, common HTTP methods, status codes, and reading a REST endpoint | | Node.js | Core module usage, async callbacks or promises, and simple Express route handling | | System Design | Describing a basic client-server flow, explaining caching at a high level, and diagramming a small feature | | AI Engineering | Calling a model API, prompt structure, and where an LLM fits inside a larger application |
How to study, in order
Working through these areas in sequence keeps each new concept grounded in something you already understand.
- Python core — get comfortable with functions, error handling, and working with lists and dicts before anything else.
- Databases & SQL — write real queries against a sample database; SELECT, JOIN, and GROUP BY are the moves that matter.
- HTTP & APIs — use a tool like curl or Postman to make actual requests; reading raw responses makes the theory stick.
- Node.js basics — build one small Express server that reads from a database; it ties together concepts from steps 1–3.
- AI Engineering — call a public model API from Python or Node.js and log the response; practical contact beats flashcards here.
- System Design — practice talking through a simple feature out loud, naming the moving parts: client, server, database, and any cache or queue.
What junior interviewers are actually watching for
At difficulty level 1–2, no interviewer expects you to have production scars or a decade of opinions. They want to see that you can read an error message and work toward a fix, that you know when to ask a clarifying question, and that your mental model of a request moving through a stack is basically correct. A candidate who says "I'm not sure, but here's how I'd find out" consistently outperforms one who bluffs.
The salary and demand data for Junior Software Engineers is rendered live below this introduction, reflecting current market rates across regions. Compensation at this level varies by location, industry, and stack, so treat any single figure you read elsewhere as a starting point rather than a ceiling.
What to study, in order
For a junior Software Engineer interview, prioritise the role's most in-demand technologies first:
- Databases & SQL
- Python
- AI Engineering
- HTTP & APIs
- System Design
- Node.js
Software Engineer salary & demand
From live job postings, the median Software Engineer salary is £55,000, across 297 recent postings. These figures are role-wide across all seniority levels, as of June 2026.
| 25th percentile | £37,500 |
|---|---|
| Median (50th percentile) | £55,000 |
| 75th percentile | £82,500 |
Practice questions
AI Engineering/llm-foundations/tokens-context
A model's context window is shared between the input (prompt) tokens and the generated output tokens — they draw from the same budget.
Show answer
True. The context window bounds the total tokens the model attends to — system prompt, retrieved context, conversation history, and the tokens it generates all draw from the same budget. If you stuff the input close to the limit, you starve the output and risk truncated completions. Budget explicitly: reserve headroom for max_tokens of output, since long outputs cost both latency and window space.
The context window bounds the total tokens the model attends to: system prompt + retrieved context + conversation history + the tokens it generates. If you stuff the input close to the limit, you starve the output and risk truncated completions. Budget explicitly — reserve headroom for max_tokens of output, and remember that long outputs cost both latency and window space.
Databases & SQL/querying/select-basics
Which keyword removes duplicate rows from a SELECT result set?
Options
DISTINCTUNIQUEGROUPDEDUPE
Show answer
DISTINCT removes duplicate rows from a SELECT result set. Writing SELECT DISTINCT collapses rows that are identical across all selected columns into a single row. UNIQUE is a column or table constraint rather than a query keyword, and GROUP BY aggregates rows instead of simply de-duplicating them.
SELECT DISTINCT collapses rows that are identical across all selected columns into one. UNIQUE is a column/table constraint, not a query keyword, and GROUP BY aggregates rather than simply de-duplicating.
HTTP & APIs/http-protocol/http-methods
Which HTTP method is intended to fully replace the resource at a known URL with the representation in the request body?
Options
- POST
- PUT
- PATCH
- GET
Show answer
PUT is the method intended to fully replace the resource at a known URL with the representation in the request body. It is idempotent, so repeating it produces the same result. PATCH only applies a partial modification, POST lets the server decide how to process the body, and GET is a read with no body semantics.
PUT replaces the target resource in full with the supplied representation and is idempotent (RFC 9110 §9.3.4). PATCH applies a partial modification, POST processes the body however the server decides (often creating a subordinate resource), and GET is a read with no body semantics.
Node.js/node-modules/commonjs
In a CommonJS module (a .js file loaded with require), which of these is a real, module-scoped variable Node injects automatically?
Options
__dirnameimport.meta.urlglobalThis.modulewindow
Show answer
__dirname is the real module-scoped variable Node injects automatically. Node wraps every CommonJS module in a function that receives exports, require, module, __filename, and __dirname as parameters, so it is available without any import. import.meta only exists in ES modules, and window is a browser global.
Node wraps every CommonJS module in a function that receives exports, require, module, __filename, and __dirname as parameters, so __dirname is available without importing anything. import.meta only exists in ES modules; window is a browser global.
Python/data-model/truthiness
Which of these values are falsy (i.e. bool(value) is False)? Select all that apply.
Options
[]0"""0"[0]
Show answer
The falsy values are the empty list [], the number zero 0, and the empty string — but not the string "0" or the list [0]. Truthiness depends on emptiness, not on the contents being zero-like, so a non-empty string "0" and a non-empty list [0] are both truthy even though they contain a zero.
Empty containers ([]), the number zero (0), and the empty string ("") are all falsy. The string "0" is a non-empty string and the list [0] is a non-empty list — both are truthy, because truthiness depends on emptiness, not on the contents being zero-like.
System Design/sd-fundamentals/horizontal-vertical
Your API runs on a fleet behind a round-robin load balancer, but users intermittently get logged out as requests land on different nodes. The team stores session state in each node's local memory. What is the single most important change to enable safe horizontal scaling?
Options
- Move session state out of the app process into a shared store (e.g. Redis) so any node can serve any request
- Enable sticky sessions on the load balancer and keep state in local memory
- Scale each node vertically with more RAM so sessions never get evicted
- Add a CDN in front of the API to cache the session responses
Show answer
Move session state out of each node's local memory into a shared store such as Redis, so any node can serve any request. The nodes are stateful today — each holds session data only it can see — which is why round-robin routing logs users out. Externalising state makes nodes interchangeable, the precondition for safe horizontal scaling, rolling deploys, and failover.
The root problem is that the app nodes are stateful: each holds session data only it can see. Externalising state to a shared store makes the nodes interchangeable, which is the precondition for horizontal scaling and for free rolling deploys and failover. Sticky sessions are a workaround, not a fix — they pin users to a node, so a deploy or crash still drops their session and they defeat even load distribution. Vertical scaling (more RAM) raises the ceiling but keeps the state trapped on one box, so the cross-node inconsistency remains. A CDN caches public, cacheable responses; per-user session data is neither, so it does nothing here.
AI Engineering/llm-foundations/sampling-temperature
At temperature = 0 a model decodes greedily — it always emits the highest-probability token. This simulates that single step over a tiny set of next-token logits. What does it print?
const logits = { cat: 2.1, dog: 0.7, bird: 1.4 };
const next = Object.entries(logits).reduce((best, cur) =>
cur[1] > best[1] ? cur : best
)[0];
console.log(next);Show answer
cat
Greedy decoding (the effective behaviour at temperature = 0) takes the argmax over the logits, ignoring the rest of the distribution. The largest score is 2.1 for cat, so that token is chosen. This is why temperature = 0 makes a model approximately deterministic for a fixed prompt and model version — it removes the sampling step. It is not a true RNG seed though: ties, hardware/kernel non-determinism, and provider-side changes can still cause drift.
Databases & SQL/schema-design/foreign-keys
What does a FOREIGN KEY constraint guarantee?
Options
- Every value in the referencing column matches an existing value in the referenced column (referential integrity)
- The referencing column values are unique
- The referencing column is automatically indexed in every database
- Both tables are stored in the same physical file
Show answer
A FOREIGN KEY guarantees referential integrity: every value in the referencing column matches an existing value in the referenced column. You cannot insert a child row pointing at a non-existent parent, nor orphan children by deleting a parent. It does not impose uniqueness, and PostgreSQL does not auto-create an index on the referencing column, though one is often added to speed joins and cascade checks.
A foreign key enforces referential integrity: you cannot insert a child row that points at a non-existent parent, and (depending on the referential action) you cannot orphan children by deleting a parent. It does not impose uniqueness, and although PostgreSQL does not auto-create an index on the referencing column, many people add one to speed joins and cascade checks.
HTTP & APIs/http-protocol/status-codes
A POST /orders request successfully creates a new order. Which status code is the most appropriate response?
Options
- 200 OK
- 201 Created
- 202 Accepted
- 204 No Content
Show answer
201 Created is the most appropriate response when a POST successfully creates a new resource, and it should carry a Location header pointing at the new order. 200 OK implies a generic success with no creation, 202 Accepted means processing is still happening asynchronously, and 204 No Content means success with no body.
201 Created signals that a new resource was created as a result of the request and should carry a Location header pointing at it (RFC 9110 §15.3.2). 200 implies a generic success with no creation, 202 means the request was accepted for asynchronous processing that hasn't finished, and 204 means success with no body.
Node.js/runtime/process-env
What is the type of a value read from process.env.PORT?
Options
string(orundefinedif unset)numberstring | numberdepending on the value- Always
undefineduntil you calldotenv.config()
Show answer
It is a string, or undefined if the variable is not set. Every value on process.env is a string — numeric-looking values like 3000 are still strings — so you must convert with Number(process.env.PORT) before using one as a number.
Every value on process.env is a string (or undefined when the variable is not set). Numeric-looking values like "3000" are still strings, so you must convert with Number(process.env.PORT) before using them as numbers.
Python/testing-idioms/string-handling
Python strings are immutable, so s[0] = 'z' raises a TypeError.
Show answer
True. Python strings are immutable, so there is no in-place item assignment and s[0] = 'z' raises a TypeError reporting that a str object does not support item assignment. Methods like s.replace(...) return a brand-new string rather than mutating the original.
str is immutable — there is no in-place item assignment, so s[0] = 'z' raises TypeError: 'str' object does not support item assignment. Methods like s.replace(...) return a brand-new string rather than mutating the original.
System Design/sd-fundamentals/cdn-edge
Your global app serves large static assets (images, JS bundles, video) and origin egress plus latency are hurting. You put a CDN / edge cache in front. Which outcomes are correct expectations of this change?
Options
- Cached assets are served from edge PoPs close to users, cutting round-trip latency
- Origin load and egress drop because the CDN absorbs repeat requests for cacheable content
- Cache-Control / TTL headers govern how long edges serve content before revalidating with origin
- Highly personalized, per-user dynamic API responses become trivially cacheable at the edge
- A bad deploy is harmless because edges instantly reflect every origin change with no invalidation needed
Show answer
The correct expectations are that cached assets are served from edge PoPs close to users (cutting round-trip latency), origin load and egress drop because the CDN absorbs repeat requests for cacheable content, and Cache-Control/TTL headers govern how long edges serve content before revalidating. Highly personalized per-user responses are not trivially cacheable at a shared edge, and edges do not instantly reflect origin changes — stale objects live until TTL or an explicit purge.
A CDN caches content at edge points of presence near users (a), so it lowers latency and offloads repeat requests from the origin, cutting load and egress (b), with freshness controlled by Cache-Control/TTL and revalidation (c). The wrong options are classic edge-caching traps. Personalized, per-user dynamic responses are generally not cacheable at a shared edge (d) — caching them risks leaking one user's data to another, so they need private/no-store handling or edge-compute personalization, not naive caching. And edges do not instantly reflect origin changes (e): cached objects live until their TTL or an explicit purge/invalidation, which is precisely why a bad asset deploy can keep serving stale content until you invalidate the cache or bust the URL.
AI Engineering/rag/rag-basics
What is retrieval-augmented generation (RAG) and what problem does it solve over just prompting the model?
Show answer
RAG retrieves relevant documents from an external knowledge source (usually via a vector search over embeddings) and injects them into the prompt as context, so the model answers grounded in that retrieved data. It solves the problem that a model's parametric knowledge is frozen at training time and can hallucinate — RAG lets you supply fresh, private, or domain-specific facts at query time without retraining, and lets you cite sources.
RAG decouples knowledge from weights: the model stays general, while a retrieval layer (embed the query, search a vector store, pass the top hits as context) supplies current and proprietary facts. The big wins are freshness, the ability to cite sources, and reduced hallucination — but retrieval quality becomes the bottleneck, so chunking, the embedding model, and reranking matter as much as the LLM itself.
Databases & SQL/querying/joins
For an INNER JOIN, swapping which table is written first (A JOIN B vs B JOIN A) changes which rows appear in the result.
Show answer
False. INNER JOIN is commutative with respect to its result set, so A JOIN B and B JOIN A return the same rows (column ordering aside). Swapping which table is written first changes nothing about which rows appear. This is not true for outer joins, where LEFT JOIN and RIGHT JOIN keep different unmatched sides.
INNER JOIN is commutative with respect to its result set: A INNER JOIN B ON … and B INNER JOIN A ON … return the same rows (column ordering aside). This is not true for outer joins — LEFT JOIN and RIGHT JOIN keep different unmatched sides.
HTTP & APIs/api-design/versioning
Which of these is an example of URI path versioning of an API?
Options
- GET /users with header
Accept: application/vnd.myapp.v2+json - GET /users?version=2
- GET /v2/users
- GET /users with header
X-API-Version: 2
Show answer
GET /v2/users is URI path versioning, because the version is embedded directly in the path. The others are different valid strategies: a query parameter like ?version=2, a custom header such as X-API-Version, and media-type versioning negotiated through the Accept header. Each trades discoverability against URL stability.
URI path versioning puts the version directly in the path, e.g. /v2/users. The alternatives are other valid strategies: a query parameter (?version=2), a custom header (X-API-Version), and media-type/content negotiation versioning via the Accept header (the most RESTful but least visible). Each trades discoverability against URL stability.