Quick Takeaways
What you'll learn in this article
- 1
In a recent interview, a candidate for a senior frontend role was interrogated for forty-five minutes on backend sync-versus-async architecture by a director who never asked about the frontend
- 2
A passive audit of the company's own web property afterwards revealed the entire frontend source code was being served as a public download, thirteen dangling subdomains, no login rate limiting, and mobile LCP at three times the "Poor" threshold
Keep reading for detailed implementation, code examples, and real-world results
The Frontend Interview That Wasn't
The interview was scheduled as a frontend conversation. The role was a frontend role. The job description was a frontend job description. The candidate prepared for a frontend interview.
What they got instead was forty-five minutes of a director defending her opinions about backend architecture โ specifically, when to use synchronous versus asynchronous APIs โ to a candidate she had invited in to talk about the frontend.
The candidate had recently read a CrashBytes piece on sync vs async in modern API development and the assumption, after the fact, is that the director had read it too, disagreed with parts of it, and wanted to make sure the candidate understood her disagreement. By the end of the call she had not asked a single question about the frontend.
The story is worth walking through, because the dynamics of that interview are not unique to that company, and because what the candidate found when they went looking at the company's frontend afterwards is the cautionary tale that the interview itself should have been.
The setup
Specific details have been anonymized. The company is a venture-backed B2C product company operating in a regulated industry. It scaled fast. Its public engineering brand is "modern." The frontend stack, as advertised in the job description, was one framework. The actual frontend stack, as the candidate would later confirm by reading the company's own JavaScript, was a different framework family entirely. That mismatch will matter shortly.
The candidate was invited to interview for a senior frontend role. The pipeline was the usual: recruiter screen, hiring-manager screen, then a panel including the engineering director.
They came in expecting questions about state management, render performance, hydration, accessibility, the tradeoffs between SSR and client-only rendering, how they thought about bundle weight on a B2C funnel, how they'd organize a design system across multiple surfaces. They had real opinions on all of it.
The director opened with: "I read your reading list. I want to push back on the sync-versus-async piece."
That was the entire interview. For forty-five minutes she litigated the article โ not to learn how the candidate thought, but to defend the architectural choices her team had made on a backend the candidate had not seen, for a role that did not involve that backend, in a session that had not been framed as a backend evaluation. When the candidate tried, twice, to pivot to anything frontend-adjacent โ "how is your design system organized?", "are you happy with your render performance on mobile?" โ she pulled it back. The async question was apparently the only question.
The candidate left the call confused, then irritated, then curious. Because the thing about insisting on a strong opinion in a forty-five-minute window is that the interviewer is implicitly asserting that the rest of the engineering posture is in good enough shape that this is what's left to argue about.
So the candidate went and looked.
A note on what "passive" means
Before walking through the findings, the methodology matters, because the line between observation and intrusion is the entire ethical posture of the exercise.
Everything in the next section was obtained the same way any new customer would obtain it. The candidate visited the company's marketing site, signed up using a disposable email, walked the new-user funnel as a real prospective customer would, and used the browser's built-in DevTools to inspect what the company was sending them. They added two pieces of standard tooling on top โ curl to re-request resources without the browser cache muddying things up, and Lighthouse to score the pages โ but neither of those tools does anything a modern browser doesn't already do internally.
No authenticated APIs were probed. No rate-limited endpoints were stressed. No payloads were sent. No fuzzers were run. No scanners walked the perimeter looking for misconfigurations. Nothing was downloaded that the company was not already, voluntarily, serving to anonymous internet traffic on the open web.
The reason this matters: every single finding below is the kind of thing the company's own engineers could have surfaced from their own laptops in a single afternoon. None of it required adversarial intent, and none of it required adversarial tooling. The findings are not the product of an attack. They are the product of looking.
What the candidate found when they looked
The next day they ran a passive security and performance audit on the company's public web properties. No payloads, no scanners hitting rate-limited endpoints, no authenticated flows, nothing that any internet user with curl and a browser couldn't do. The point wasn't to break into anything. The point was: if the company's backend architecture opinions were so strong they crowded out an entire frontend interview, the frontend itself ought to be in beautiful shape.
It was not.
Finding one: the entire frontend source code was a public download
Every JavaScript bundle the production web app shipped came with a .map file served at the same URL. Source maps, in modern web build pipelines, exist to help developers debug minified production code by mapping line numbers back to the original source. They are useful internally. They are catastrophic externally โ if the build ships them with sourcesContent populated, which embeds the original TypeScript inside the map.
The company's were. All of them.
A single curl GET per .map URL, piped through python -c 'import json' to extract the sourcesContent array, recovered 148 original TypeScript source files in under five minutes. The design system. The payment integration wiring. The file-upload validation. Internal API endpoint paths. The controller for the customer portal. Codenames for projects nobody was supposed to know existed. The build manifest exposed twenty-three entry-point bundles, including ones with names like admin and the internal-tools surface.
Source files recovered
148
from public .map URLs in under five minutes, using only curl and a one-line Python script
This is not subtle. This is not a defense-in-depth nicety. This is shipping the source code of a product to anonymous internet users and calling it a production deploy.
How common is this, actually?
The dirty secret of modern frontend builds is that the bundler defaults make this easy to do by accident. Webpack, Vite, esbuild, Turbopack, Rollup, and Next.js all emit source maps by default in their development mode, and most of them emit them in production unless explicitly told not to. Several of them emit maps with sourcesContent populated unless the developer overrides the default โ which is the configuration that makes the maps catastrophic rather than merely useful.
The candidate, who has spent the last several years auditing public web properties for the kind of unforced errors that make engineering directors look bad, has informally tracked source-map exposure across a few hundred companies they have encountered in interviews, on customer calls, and in the ordinary course of being a customer of the modern internet. The pattern is that "public-facing web property serves its own source code" is roughly a one-in-three problem at small-to-midsize venture-backed companies, drops to roughly one-in-five at large enterprises with mature security functions, and drops to nearly zero only at the handful of companies where someone on the infrastructure team has made it their personal mission.
Estimated rate of full source-map exposure in production, by company segment
| segment | exposed |
|---|---|
| Seed/Series A | 41 |
| Series B/C | 33 |
| Series D+ private | 22 |
| Public co (mid-cap) | 18 |
| Public co (mega-cap) | 7 |
| FAANG-tier | 2 |
(Caveat: these are informal observations, not a methodologically rigorous study. The point is the order of magnitude, not the precise digits. A formal study would be welcome and would, the candidate suspects, find numbers in this neighborhood.)
What the source maps gave away, in the company's specific case, went beyond "someone could read your TypeScript." Inside the recovered source were inline comments referencing the company's experiment platform, the internal name of a not-yet-launched product, hard-coded staging URLs that were still resolvable, and one file containing a commented-out console.log of an environment variable name that strongly implied the existence of an admin-only API parameter. None of that, individually, is a vulnerability. Together, it is a map of the building. An attacker doing reconnaissance for a real engagement would not pay a pentest firm for what was sitting at /static/js/*.js.map.
Finding two: thirteen dangling subdomain takeover candidates
A quick certificate-transparency-log enumeration of the domain turned up thirteen *.theirdomain.com records pointing at deleted Heroku apps, deleted AWS ELBs, and deleted CloudFront distributions. Including the one literally named staging. Each of those is a candidate for subdomain takeover. The Heroku ones are the highest-risk โ if an attacker can claim the deleted Heroku app identifier, they get to serve content from a real *.theirdomain.com URL with a valid TLS certificate. Perfect phishing infrastructure for spear-phishing the company's own employees or its customers.
Thirteen. The number itself was the message: nobody owned DNS hygiene.
Where the thirteen dangling subdomains pointed
| Name | Value |
|---|---|
| Deleted Heroku apps | 6 |
| Deleted AWS ELBs | 4 |
| Deleted CloudFront distributions | 2 |
| Deleted GitHub Pages site | 1 |
The mechanics of subdomain takeover, for the reader who has not had to think about this in a while, are mundane: a company creates a subdomain like promo.theirdomain.com and points it at a Heroku app named their-promo-2024-q3.herokuapp.com. The promo ends. The team deletes the Heroku app. Nobody deletes the DNS record. Now the DNS record points at a Heroku app name that no longer exists โ which means anyone can register a new Heroku app with that name. The attacker registers it. They serve whatever they want from a URL that ends in .theirdomain.com and gets an automatic valid TLS certificate from Heroku's automated cert provisioning. From the user's browser, this is indistinguishable from a legitimate page from the company.
This is the infrastructure of credible spear-phishing. An attacker who claims staging.theirdomain.com can send an email to the company's own engineers saying "hey, can you log into the staging environment and check this?" with a link to a page that looks exactly like the company's staging login. The engineer logs in. The attacker has credentials. From a UX perspective the attack is invisible, because the URL is real.
A single CT-log query is what surfaced this. The CT logs are public. They exist precisely so that anyone can audit what certificates have been issued against any domain. Nobody at the company had run that query in the lifetime of those thirteen records.
Finding three: no rate limit on the login endpoint
Ten consecutive failed login attempts against the same email, from the same IP, returned ten clean HTTP 401s. No lockout. No 429. No CAPTCHA. No exponential backoff. The password-reset endpoint had the same posture, which made it an email-bombing surface against any real customer email anyone could guess.
For context: rate limiting on authentication endpoints is not advanced security engineering. It is the kind of thing every framework's official recommended-config document has covered for the better part of fifteen years. Express, Django, Rails, ASP.NET, FastAPI, Phoenix, Laravel โ every one of those documents how to do it, and most of them ship plug-and-play middleware for it. The libraries that handle it are well-tested, free, and require roughly forty lines of code to wire up.
ComparisonCard requires either 'items' prop or both 'leftSide' and 'rightSide' props
The absence of rate limiting in 2026 on a B2C login endpoint that handles real financial transactions is the kind of thing that, when it shows up in a post-incident review, the reviewing engineer asks "who deleted this, and when?" โ assuming, charitably, that the limit was once there. The candidate's working theory is that it was never there. The company was big enough, and old enough, that the absence is a choice, not a startup-era omission.
This is the third finding in a row that pattern-matches to "nobody internally has the standing to push the basic-hygiene work, because the directors are busy litigating sync vs async in interviews."
Finding four: mobile LCP at ten to twelve seconds
This was the one that, given the interview that had just taken place, made the candidate laugh out loud.
Lighthouse โ Google's first-party, free, public performance tool, the one any of the company's engineers could run from their own laptop in thirty seconds โ reported Largest Contentful Paint between 10.1 and 12.0 seconds on three of the four funnel-critical pages tested. Google's "Poor" threshold is 4.0 seconds. The company was exceeding the poor threshold by a factor of two and a half to three.
Mobile Largest Contentful Paint (seconds) vs Google thresholds
| page | lcp |
|---|---|
| Google Good | 2.5 |
| Google Needs Improvement | 4 |
| Industry P75 (B2C funnel) | 4.8 |
| Their landing page | 10.1 |
| Their pricing page | 11.4 |
| Their signup form | 12 |
The architectural cause was textbook: 4.1 megabytes of vendor JavaScript shipping synchronously on every route, then a render path that awaited the initial data fetches before drawing anything. The user sees a spinner because the page literally cannot interact until the bundle has downloaded, parsed, executed, and resolved its initial network requests.
This is the sync-blocking-on-data anti-pattern. It is, line for line, the thing the sync-versus-async article had warned against. The director who spent the interview defending her company's async posture was, by the company's own production code, the steward of a render path that exemplified the sync-blocking pattern in its purest form.
She wasn't wrong about her backend's async behavior, as far as anyone outside the company can tell. The candidate never saw the backend. But she was defending a position on async that her own frontend rendered moot. "We use async API calls" is not a defense if the render path then awaits them. Async fetch plus sync render equals sync render. The async work has to happen while the user is already seeing meaningful content, or the asynchronicity is purely a server-side bookkeeping detail the user never benefits from.
This was knowable from the outside, by any candidate, in fifteen minutes of DevTools. It was demonstrated, by the company's own production traffic, every time a mobile customer tried to use the funnel on their phone.
A concrete picture of the render path
Here is, in pseudocode, what the company's main entry component was doing โ reconstructed from the recovered source maps. The exact framework has been abstracted out, because the pattern is identical regardless of which one you prefer.
// Their entry component, roughly
function AppShell() {
const user = useFetch('/api/me'); // network
const config = useFetch('/api/config'); // network
const experiments = useFetch('/api/exp'); // network
if (!user.data || !config.data || !experiments.data) {
return <FullPageSpinner />;
}
return <Router user={user.data} config={config.data} experiments={experiments.data} />;
}
Three sequential awaits โ the spinner stays up until the slowest of the three returns. None of the three is independently necessary to begin painting the page. The user's identity is needed for the account menu in the header, but not for the marketing hero. The config blob is needed for feature flags, but the marketing hero has no feature flags. The experiments fetch is needed for A/B testing, but A/B-testing the spinner is pointless.
The fix is not exotic. Render the marketing hero immediately from cached or embedded HTML, hydrate progressively, fetch user-specific data in parallel while the user is already reading the page. Modern frameworks ship this pattern as the default โ Next.js calls it streaming, React calls it Suspense boundaries with use(), SvelteKit and Remix have their own names for it. What the company shipped was the 2018 pattern, in 2026, on top of a self-described "modern" stack.
This is why the candidate laughed. The whole interview was a debate about async patterns conducted by a person who, by the evidence of her own production frontend, was responsible for the most aggressively synchronous render path the candidate had seen in six months of looking.
A severity scorecard for the findings
The four findings are not equal. They have different exploitability, different blast radius, different reversibility, and different cost to fix. Putting them on one chart helps a reader who has not had to triage findings before see how to weight them.
The reason the source-map finding is rated highest on both axes is that it is trivially exploitable (one HTTP GET), highly leveraged (it informs every other attack), and completely silent on the company's side. There is no logging infrastructure that fires when an attacker downloads a static file from your CDN. The company would never know it had happened.
The LCP issue is not a vulnerability at all โ it is a revenue bug. On a B2C funnel, every second of LCP costs measurable conversion, and a P75 in double-digit seconds is the kind of number that turns up in a board deck as "why are our acquisition costs going up while our funnel conversion is going down." A reasonable estimate of the lost revenue from that single finding exceeds the salary of every engineer in the company.
Why the combination is the tell
Any one of those findings, in isolation, would be a normal week at a normal company. Source maps slip out. DNS hygiene rots. Rate limits get rolled back during an incident and never reinstated. LCP regresses when someone ships a big vendor library and the perf monitoring is on the wrong route.
The signal is not any individual finding. The signal is the combination, because the combination tells you what kind of conversations are not happening internally.
If the source maps were the only finding, the inference would be: "the build team is small and the security team is small and nobody has gotten around to it." If the subdomain count was the only finding, the inference would be: "there's no DNS owner, which is annoying but recoverable." If the rate limits were the only finding, the inference would be: "the auth team is busy with something else, and this is in the backlog." If the LCP were the only finding, the inference would be: "they shipped a vendor library three months ago and nobody has checked the dashboards."
All four together tell you that the company has no internal champion for end-to-end frontend quality. There is no engineer with the standing, the focus, or the authority to walk the perimeter and notice. Worse โ and this is the candidate's stronger inference โ there is no engineering leader who would welcome that engineer if they showed up. Because if such a leader existed, they would not be spending forty-five minutes of a frontend interview re-litigating a blog post about backend architecture.
The interview, in other words, was not a fluke of one director's bad afternoon. It was a faithful representation of how technical decisions get made at that company. The directors decide what is important, the engineers implement it, and the rest of the perimeter โ the actual user experience, the actual security posture, the actual production code that customers touch โ sits in the gap.
The pattern
The candidate wrote up the findings โ passive only, no payloads, single-day engagement โ and sent the deliverable to the company's CTO with a polite note. As of this writing, no response. None is particularly expected.
The pattern, though, is worth naming, because it is not unique.
A senior engineer or director attaches their identity to a specific architectural opinion, then uses the interview process to recruit candidates who will affirm that opinion โ not to find engineers who will strengthen the actual product. The interview becomes a loyalty oath rather than an evaluation. The candidate is graded on alignment, not on signal.
This is bad for the candidate, because the role they're hired into may be a real role, but the interview gave them no information about whether they would be allowed to do it. It is bad for the company, because the engineers it selects will share the founders' or the directors' blind spots โ which means the blind spots will not get fixed.
In this specific case, the blind spot was vivid. The frontend was unsalvageable in three specific ways that any new frontend hire could have spotted in the first week. A new frontend hire who came in wanting to fix it would, presumably, have spent her first months pushing on source-map exposure, the sync-blocking render path, and the missing rate limits. Whether she would have been heard is a different question. The interview the candidate sat through suggests the answer.
A taxonomy of bad-faith interviews
The "loyalty-oath" interview is one of several recurring failure modes the industry has invented over the past decade. They tend to arrive in waves, loosely correlated with whichever hiring panic is current. CrashBytes has covered several of these patterns at length โ the live-coding interview fallacy, the broader case for rethinking how the industry interviews, and the related question of how AI-driven hiring tooling is reshaping the recruiter side of the table. A short field guide:
What unifies the taxonomy is that none of these interview styles is actually about the job. The whiteboard interview is not about whether the candidate can do the work; it is about whether the candidate is intimidated by the panel. The take-home black hole is not about evaluating the take-home; it is about filtering for candidates whose desperation outlasts the company's apathy. The loyalty oath is not about technical opinions; it is about whether the candidate will defer to the interviewer's worldview without pushback.
When the cumulative answer to "what is this interview actually testing for?" is something other than "the work," the interview is doing damage on both sides of the table. The candidate spends a day proving the wrong thing. The company ends up with engineers who passed the wrong test, which is to say, who will fail the real one.
The macro picture
It is tempting, in 2026, to read all of this as a story about one mid-stage company with a director who had a bad day. The candidate would gently push back. The macro hiring environment is currently producing more of this, not less.
When Fortune 500 companies are cutting 15โ25% of engineering headcount on the expectation that AI tooling will absorb the work, and when adjacent industries are seeing hiring fall to decade lows in roles that used to be safe, the supply-demand curve in technical hiring tilts hard toward the company. A candidate who walks out of a forty-five-minute loyalty oath in 2020 had ten other interviews on the calendar. The same candidate in 2026 may have one other interview on the calendar. The pressure to nod along, to write the nice follow-up email, to not write the article you are now reading, is substantially higher.
The other side of the curve, though, is that fewer hires means each hire matters more, and a senior engineer hired into a role where they cannot do the work is a hire the company is going to regret in twelve months. The forty-five-minute loyalty oath does not save the company any money. It defers the cost of getting hiring wrong by exactly the length of the candidate's first review cycle, and then the cost lands on the same desk it would have landed on anyway, except now there is a real person attached.
The point is not that the macro environment justifies bad interviews. The point is that the macro environment is what makes them dangerous. When candidates feel they cannot afford to walk away, hiring panels lose the external feedback loop that used to discipline them. That feedback loop โ the candidate who quietly tells their network "don't apply there, that panel is broken" โ is what has historically saved companies from their own worst interview habits. In 2026, with fewer candidates and more roles ghosted, that feedback loop is quieter than it has been in a decade.
The companies that survive the next few years on the talent dimension are going to be the ones that notice their own interview pathology before the market does it for them.
The lesson โ for candidates
If an interview for a frontend role is entirely about the backend, that is signal. Not necessarily disqualifying signal, but signal. The most common reason an interviewer pulls a conversation away from the role they're hiring for is that the role they're hiring for has problems the interviewer does not want to discuss.
A candidate is allowed to redirect. "I'd love to come back to that, but I'd also like to spend some of our time on the work I'd actually be doing โ can we talk about your current frontend?" is a polite, professional, normal question that an interviewer who is interviewing in good faith will welcome. An interviewer who refuses to pivot has told the candidate something important about how their future role will be conducted.
A candidate is also allowed, between rounds, to spend thirty minutes with Lighthouse and the company's public web property and to read the build manifest. None of this requires special access. None of it is hostile. All of it is publicly served by the company itself, voluntarily, in the course of running its business. It is exactly the diligence anyone would do on any vendor they were considering paying.
A short, opinionated, non-exhaustive due-diligence checklist for candidates who have an hour to spend between the recruiter call and the final round:
- Lighthouse the funnel. Run mobile Lighthouse against three pages โ the landing page, the pricing page, the signup form. If LCP exceeds 6.0 seconds on any of them, you have a real conversation to have about priorities in the role.
- Check the build manifest. Open DevTools, Network tab, look at the JS bundle URLs. If you see .map files served alongside the bundles, fetch one. If sourcesContent is populated, you now have the company's frontend source code, which means anyone does.
- Read the build for surprises. What framework is actually shipping? Does it match the job description? Is the bundle a single monolith, or has someone done route-level code splitting? Is there a service worker? Are the bundles versioned with content hashes, or are they bundle.js?
- Run a CT-log query against the apex domain. crt.sh will do this for free in a browser. Eyeball the list for subdomains pointing at deleted infrastructure. Anything ending in .herokuapp.com, .s3.amazonaws.com, .cloudfront.net, or .github.io that you cannot resolve to an active resource is worth noting.
- Check a non-existent endpoint. Hit /api/healthz or /api/version or /.well-known/security.txt. A security.txt with a real email address tells you someone is paying attention. The absence of one tells you no one has bothered.
None of these is a substitute for the interview itself, and none is a reason to decline an offer. They are calibration. They help the candidate ask better questions, ask them earlier, and read the answers more accurately.
The lesson โ for hiring managers
If you are interviewing for a frontend role: ask about the frontend.
If you have strong opinions about your backend architecture and you want a new frontend engineer to share them, you can mention that in the offer conversation. The interview is the wrong room for the litigation.
And if your interview consists of asking the candidate whether they agree with you about an architecture they have not seen, on a system they will not work on, while your actual production frontend ships its source code as a public download โ the candidate is being polite by not pointing this out, and they are correct to keep that observation to themselves until after the process is complete.
Some of them, later, write about it.
A subtler version of the same lesson: the interview is a two-way audit. The candidate is auditing the company as much as the company is auditing the candidate. If the company's leadership cannot tolerate that two-way audit โ if a question like "can we talk about your render performance on mobile?" is treated as a challenge to be deflected rather than a question to be answered โ the company is signaling to the candidate, accurately, that the company will not tolerate disagreement once the candidate is inside. Which means the candidate, if they are senior, will either decline the offer or accept it and spend their first year resenting the constraint. Either outcome is more expensive than the cost of letting the candidate ask the question.
Footnote, for the engineers who read this far
If you are reading this and recognize your own build pipeline in the source-map description, the fix is small and the fix is fast. Two options:
-
Disable source map emission in the production build. One config flag in webpack, Vite, Turbopack, esbuild, or whichever bundler you use. Re-deploy. You lose nothing the user sees; you lose your ability to debug minified prod traces in DevTools, which you weren't doing anyway.
-
Keep emitting maps but never serve them to the public. Upload them to your error reporter (Sentry, Datadog, Rollbar all support this natively) as a build artifact, and configure your CDN to reject any request for *.map at the edge. This is what production-grade stacks do. Your error reporter gets symbolicated stack traces; the public internet does not get your source tree.
Either fix is an afternoon. The cost of not doing it is that any stranger on the internet โ including one the company just interviewed and declined โ has the entire client codebase by the time they get home.
The trade-off between the two options is worth thinking through once, explicitly, before picking:
ComparisonCard requires either 'items' prop or both 'leftSide' and 'rightSide' props
The middle option โ "we'll just put the maps behind a header check so only our employees can fetch them" โ is not on the chart because it is not a real option. Any access control that depends on a request header an attacker controls is, by construction, not access control. If the maps are served at a URL, they will be fetched at that URL. The only safe configurations are "do not generate them in the build that ships" and "generate them but route them somewhere the public web cannot reach."
While you are in there: rate-limit the login endpoint, query the CT logs once a quarter and prune the dead subdomains, and run Lighthouse against your funnel before you ship the next feature. None of those is hard. The reason they get skipped is not capacity. It is attention. The bigger conversation about where engineering attention is going in 2026 is worth having, because if AI tooling is going to claw back any of the time engineers used to spend on basic-hygiene work, the only way it does so is if someone on the team has explicitly pointed it at the basic-hygiene backlog. Otherwise the time gets reabsorbed into the same architectural debates that filled the previous era.
One last thing
The candidate, in the end, did not get an offer. They withdrew before the final round. They sent a polite note saying the role did not feel like a fit. The recruiter, who had been pleasant throughout, wrote back with a generic acknowledgement and the standard "we'd love to keep you in mind for future roles." Neither side mentioned the actual reason.
A few weeks later, the candidate accepted a senior frontend role at a different company in the same industry. The new company's interview had asked about the frontend. The new company's source maps were not exposed. The new company's Lighthouse scores were not catastrophic. The new company's interviewer had no reading list and no axe to grind. They asked the candidate how they had organized a design system across multiple surfaces, what the candidate thought about render performance on mobile, how they thought about state management at scale, and what they would change about the new company's funnel if they had a quarter to do it.
The candidate gave real answers. The interviewer asked follow-up questions. The conversation lasted forty-five minutes. At the end of it, both sides felt they had learned something about the other, and an offer arrived the following week.
The whole thing, the candidate later reflected, had been remarkably unremarkable. Which was the point.

