GitHub runs a fully remote loop focused on pragmatic systems thinking and written communication. Design discussions often take an async RFC shape, and the behavioural thread carries the Microsoft-style Growth Mindset framing. Writing clearly about technical decisions is a real part of the assessment.
Process timeline
Reported timeline: 2-4 weeks
1
Recruiter screen
Background and remote fit.
2
Technical
Pragmatic coding and systems reasoning.
3
Design and writing
RFC-style design and clear written explanation.
4
Behavioural
Growth Mindset, collaboration, and async working.
What GitHub looks for
What they value
Clear technical writing and documentation
Pragmatic systems decisions over theory
Comfort collaborating across time zones
Culture signals
Strong async, written-first communication
Developer empathy across the platform
Growth Mindset and openness to feedback
Reported questions
Questions candidates report for this role at this company.
As asked
Explain when React Server Components are the right tool and when they are not. What changes in your mental model versus traditional React?
Sample answer outline
RSCs run on the server, never ship to the client, and can directly read from the database or filesystem. Good fit: pages that compose lots of data and want zero client JS for that composition (dashboards, content sites, lists). Not a fit: highly interactive UI (charts you can drag, editors, live forms) which still needs client components. The mental model shift: split your tree into server and client boundaries, pass data down as props (serialisable only), avoid mixing concerns. Pitfalls: passing event handlers across the boundary, accidental waterfalls, and the suspense story for streaming.
Expect these follow-ups
What is the hydration story for a page that mixes RSC and client components?
When does RSC make a page slower, not faster?
How does this change with Partial Prerendering?
reactrscnext-js
As asked
Tell me about a feature you owned from problem framing through to production rollout. Walk through the technical and the non-technical decisions.
Sample answer outline
Pick a feature with measurable impact. Cover: how the problem was framed (and how you sharpened the brief), the technical decisions you made (database changes, API shape, UI architecture, rollout plan), the collaborations (design, product, data), and the rollout (canary, metrics watched, incidents during launch). Land on the outcome and what you would do differently. Strong full-stack candidates make non-glamorous decisions sound load-bearing: schema migration ordering, the cache invalidation that you almost missed.
Expect these follow-ups
What is the part of the feature you are least proud of?
How did you decide what to build vs what to defer?
Did the metric actually move?
ownershipdeliverycross-functional
As asked
Implement cursor-based pagination for a feed API in TypeScript. Explain why cursor beats offset for this case.
Sample answer outline
Offset pagination is O(offset) per page on the database (the query still scans the skipped rows) and breaks when items are inserted or deleted mid-paging. Cursor pagination uses an opaque token encoding the last-seen sort key (e.g. created_at + id for uniqueness). The next query is WHERE (created_at, id) < (last_created_at, last_id) ORDER BY created_at DESC, id DESC LIMIT N. Constant time per page, stable under inserts. Encode the cursor as base64 of a small JSON object so clients treat it as opaque. Discuss edge cases: ties on the sort key, bidirectional paging.
Reference implementation (typescript)
type Cursor = { createdAt: string; id: string };
export function decodeCursor(token: string | null): Cursor | null {
if (!token) return null;
return JSON.parse(Buffer.from(token, "base64url").toString());
}
export function encodeCursor(c: Cursor): string {
return Buffer.from(JSON.stringify(c)).toString("base64url");
}
export async function fetchFeed(after: string | null, limit = 20) {
const cursor = decodeCursor(after);
const rows = await db.query(
`SELECT id, created_at, body FROM posts
WHERE ($1::timestamptz IS NULL OR (created_at, id) < ($1, $2))
ORDER BY created_at DESC, id DESC LIMIT $3`,
[cursor?.createdAt ?? null, cursor?.id ?? null, limit],
);
const last = rows[rows.length - 1];
return {
items: rows,
nextCursor: last ? encodeCursor({ createdAt: last.created_at, id: last.id }) : null,
};
}
Expect these follow-ups
How do you implement bidirectional pagination (back and forth)?
What if the sort field is a non-unique field like score?
When is offset still the right choice?
paginationapi-designsql
As asked
A page loads in 4 seconds. The database shows 200 queries per page load. You suspect an ORM N+1. Walk me through how you would fix it.
Sample answer outline
Confirm the diagnosis: turn on query logging, look at the call site that generates the chatty queries. Usually a loop that accesses a lazy-loaded relation. Fix with the ORM's eager loading primitive (include, with, populate, prefetch_related). Alternatively, batch the lookup with a single IN query. After the fix, verify the page is one or two queries. Watch for the second-order N+1 where the eagerly loaded set itself has a lazy relation. For React/Node: use a dataloader to batch requests inside a request scope.
Reference implementation (typescript)
// Bad: N+1 (one query per post for its author)
const posts = await prisma.post.findMany({ where: { feed: feedId } });
for (const post of posts) {
const author = await prisma.user.findUnique({ where: { id: post.authorId } });
// ...
}
// Good: eager load in a single query
const posts = await prisma.post.findMany({
where: { feed: feedId },
include: { author: true },
});
// Alternative: batch with DataLoader inside a request scope
const userLoader = new DataLoader<string, User>(async (ids) => {
const users = await prisma.user.findMany({ where: { id: { in: [...ids] } } });
const byId = new Map(users.map((u) => [u.id, u]));
return ids.map((id) => byId.get(id)!);
});
Expect these follow-ups
Why does an ORM lazy-load by default?
When is a JOIN slower than two queries?
How would you catch an N+1 in CI before it ships?
ormn-plus-oneperformance
As asked
Walk me through what happens when a user loads a page in an app you own end to end. I want you to cover both the server side, where your backend generates the response, and the client side, where the browser turns it into pixels, and the choices that span both.
Sample answer outline
A full-stack engineer should narrate both halves and the seam between them. Server side: the request hits your application, which may run middleware, query the database, render markup or serialise data, and set caching and security headers. The response then travels back, and the browser parses HTML into the DOM, builds the CSSOM, runs JavaScript, and lays out and paints. The interesting decisions span both: what you render on the server versus hydrate on the client, how cache headers and a CDN change the second visit, how the payload shape avoids client over-fetching, and where authentication is checked. The strong answer shows you reason about the whole round trip rather than treating frontend and backend as separate worlds.
Expect these follow-ups
Which work do you keep on the server and which do you defer to the client?
How do cache headers and a CDN change the second page load?
Where in this flow do you enforce authentication?
browser-internalsfull-stackssrcaching
As asked
As a full-stack engineer, how do React Server Components change how you connect the UI to your backend and database? Walk me through the data access pattern, what stays on the server, and the mistakes you would guard against.
Sample answer outline
From a full-stack perspective, server components collapse part of the API layer: a server component can query the database or call an internal service directly, so for read paths you often do not need a separate client-side fetch and an endpoint to back it. Keep secrets, database clients, and heavy data access strictly on the server side of the boundary, and pass only the plain data the client needs down as props. For writes, use server actions or API routes, and keep the client component thin. The mistakes to guard against are leaking server-only modules or credentials into client code, creating accidental waterfalls by chaining dependent server queries, and over-fetching by sending whole records to the client when a slice would do. The full-stack signal is owning the data path from database to rendered UI and deciding deliberately what crosses the network.
Expect these follow-ups
When do you still need a separate API endpoint with server components?
How do you keep database credentials from leaking into the client bundle?
Where do server actions fit for write operations?
reactrscfull-stackdata-fetching
Full-stack engineer interview detail at GitHub
How the GitHub loop applies to Full-stack engineer candidates
GitHub is a big-tech employer headquartered in San Francisco, and the same 4-stage process described above is what a full-stack engineer candidate walks through, with the technical stages tuned to the engineering discipline. GitHub runs a fully remote loop focused on pragmatic systems thinking and written communication. Design discussions often take an async RFC shape, and the behavioural thread carries the Microsoft-style Growth Mindset framing. Writing clearly about technical decisions is a real part of the assessment.
For a full-stack engineer, the load concentrates on technical and design and writing. Those are the stages where the engineering signal is read most closely, so they are where preparation pays off most. The non-technical stages (recruiter screen and behavioural) still gate the offer, but they assess fit and communication rather than role-specific depth.
What the full-stack engineer question mix signals
The 6 most-reported full-stack engineer questions cluster around frontend (3), backend (1), behavioural (1). That distribution is the clearest read on what GitHub actually probes for this role: the more a topic recurs, the more reliably it shows up in the loop, so it is worth weighting practice the same way.
The set spans a easy-to-medium difficulty range, topping out at medium problems. Beyond the headline topics, the long tail touches databases, so a full-stack engineer who only drills the top area will still hit unfamiliar ground in the onsite.
What moves a full-stack engineer offer forward at GitHub
Across the loop, the traits that consistently move a GitHub full-stack engineer offer forward are clear technical writing and documentation, pragmatic systems decisions over theory, and comfort collaborating across time zones. These are not abstract values; interviewers score against them, so a full-stack engineer who demonstrates them explicitly — naming the tradeoff, stating the assumption, checking the edge case out loud — reads stronger than one who only reaches the right answer silently.
The behavioural and culture stages are checking for strong async, written-first communication, developer empathy across the platform, and growth mindset and openness to feedback. For a full-stack engineer, the most credible way to show these is through specific, recent examples from real engineering work rather than rehearsed generalities.
How to read the full-stack engineer salary band
The salary signal shown for this role is the approximate senior median of $274,000 in San Francisco, reported as total compensation including bonus and equity and sourced from BLS, ONS, and Levels.fyi reference data. It is a market band for the full-stack engineer role and city, not a GitHub offer.
San Francisco carries a cost-of-living index of 112 on the scale where New York City equals 100, so read the headline figure alongside that index when comparing it with another market. Individual pay at GitHub varies by level, team, equity refresh, and negotiation, which the open salary breakdown for this role lays out city by city.