The Engineering Hub-City Teardown: Five Metros to One Defended Pick
Turn five shortlisted metros into a one-page ranked comparison with OEWS-anchored pools, lead depth, rival demand, renewal, ramp, and a pick with its reversing fact.
Key takeaways
- In Refolk's index, 420,743 US profiles are titled Software Engineer or Software Developer, about 25.4% of the 1,654,440 official national count for SOC 15-1252, so raw profile counts need roughly a 3.9x multiplier before they sit on the official scale.
- A 40-person engineering hub needs 6 to 9 local lead seats: 6 at a span of 8 reports per manager, and 9 if first-time managers carry only 3 to 5 reports each.
- Refolk's index holds about 6.2 software engineer profiles per engineering lead title in the US, close to the recommended span of 6 to 8, which leaves little slack for a new hub hiring its leads locally.
- Canada holds 11.3% of the US software engineer count in Refolk's index but only 8.3% of its lead-title count, a lead pool 27% thinner than headcount predicts, which shows a market can win on headcount and lose on leads.
- Rank two metros only when their 95% intervals do not overlap: Pittsburgh at an RSE of 3.4% is about plus or minus 500, while Salt Lake City at 5.1% is about plus or minus 1,440.
- Lock every metro to its OMB Bulletin 23-01 MSA before comparing releases, because Boston's software developer estimate fell about 23% across releases that coincided with the switch from NECTA to MSA definitions.
This is a working method for choosing a city for an engineering hub when leadership expects the recommendation to hold up after someone asks where each number came from. It is for strategy teams, talent-intelligence analysts, and operators running a second engineering office location analysis. It follows one five-metro comparison from the shortlist through to a ranked one-page table, a written pick, and the single fact that would reverse that pick. My wrong turns along the way are included.
What a defensible hub comparison has to show
A defensible hub comparison is one page, with one row per metro and five columns, and every column traces to a named source. The five columns are: an official role-level pool, local lead depth, rival demand, talent renewal, and a staffing-ramp estimate. City-level developer totals and lifestyle rankings stay off the page, because they don't decide whether a 40-person hub gets staffed.
The reason is structural. The pool for a specific role mix is smaller than the city total. Two things sink new hubs, and headline counts show neither of them:
- too few local people who can run the first teams
- too many established employers hiring the same profile
The page has to measure both.
Evidence layers behind each metro row
- Official baselineOEWS employment, RSE and location quotient for the role's SOC code, per MSA
- Reconciled profile poolTitle-plus-skill profile count divided by the OEWS figure for the same metro
- Lead depthLocal manager, director and head-of-engineering profiles against 6 to 9 seats
- Rival demandDeduplicated list of employers hiring the same profile now
- Renewal and rampCIP 11 completions, graduate in-migration, and a manager-gated hiring ramp
The worked example
The shortlist is Austin, Salt Lake City, Pittsburgh, San Jose, and San Francisco. The role mix is anchored on SOC 15-1252, Software Developers, because that is where most of a 40-person engineering hub sits. SOC (Standard Occupational Classification) is the federal code system that the official employment statistics are published under.
One limit up front. In this pass, the index queries could only filter by country, so I couldn't split profile counts by metro. Where a column is empty at metro level, I say so and show how to fill it on your own shortlist. I'd rather hand you a method with a visible gap than a ranking with a hidden one.
Which boundary and occupational code to anchor each metro on
Anchor every metro to its Metropolitan Statistical Area (MSA) as defined in OMB Bulletin 23-01, and every role to an SOC code. The MSA is the only boundary with an official occupational baseline. The May 2024 OEWS estimates use the 23-01 delineations, which OMB announced as effective in July 2023.
OEWS (Occupational Employment and Wage Statistics) is the BLS program that publishes employment and wages by occupation. It covers approximately 530 metropolitan and nonmetropolitan areas. For each metro and code, the area pages give employment, the employment RSE, employment per 1,000 jobs, the location quotient, and wages. Two of those terms need definitions:
- Location quotient (LQ): a metro's concentration of an occupation divided by the national concentration. Above 1 means the occupation makes up a larger share of local jobs than it does nationally.
- Relative standard error (RSE): the standard error divided by the estimate. It tells you how much sampling noise sits inside the number.
No public source measures how a role-level count changes between an MSA, a Combined Statistical Area, and a commute radius. You can draw a commute radius as context, but nothing official exists to reconcile it against. Boundary changes also matter on their own: a market can look deeper or tighter just because counties were added or removed. A practitioner comparing Boston across releases runs straight into this, which the failure-modes section covers.
Here is the official layer for the five metros, from the May 2023 OEWS metro pages for 15-1252:
| Metro | Employment | RSE | LQ |
|---|---|---|---|
| San Jose | 96,590 | 1.5% | 7.75 |
| San Francisco | 83,920 | 2.0% | 3.18 |
| Austin | 26,850 | 2.9% | 1.99 |
| Salt Lake City | 14,450 | 5.1% | 1.65 |
| Pittsburgh | 7,490 | 3.4% | 0.64 |
Wrong turn one: ranking on this table
My first draft ranked the five in exactly this order and stopped there. That is the headline-count ranking. It answers "where are the most software developers employed?", not "where can I staff a hub?"
The LQ column already shows the problem. San Jose's 7.75 means software developers make up a share of local jobs nearly eight times the national share. That tells you how deep the market is and nothing about whether you can get access to it. Pittsburgh's 0.64 means the occupation is a smaller share of local jobs than it is nationally. Both numbers are inputs to the later columns, not answers in themselves.
Is the gap between two metros real or inside the noise?
Treat two metros as different only when their 95% intervals don't overlap. The interval is the estimate plus or minus 1.96 standard errors, and the standard error is the RSE times the estimate. This is a working rule, not a published one. The only documented RSE cutoff governs whether a number gets published, and nobody has published a threshold for ranking two metros against each other.
The publication rule is that "employment and wage estimates must have relative standard errors of less than 50% in order to be published." Beyond that, BLS advises particular care with small estimates, or with small differences between estimates, when the sampling error is relatively large. The interval check turns that advice into something you can apply row by row.
| Metro | OEWS employment | RSE | 95% interval |
|---|---|---|---|
| Austin | 26,850 | 2.9% | 25,330 to 28,370 |
| Salt Lake City | 14,450 | 5.1% | 13,010 to 15,890 |
| Pittsburgh | 7,490 | 3.4% | 6,990 to 7,990 |
| San Francisco | 83,920 | 2.0% | 80,630 to 87,210 |
San Jose isn't in the table because, at 96,590 with an RSE of 1.5%, it sits nowhere near another row. Its interval can't change any ordering.
In this shortlist no pair overlaps, so the official order is real. That won't hold on every shortlist. What surprised me is that a bigger metro is not automatically easier to tell apart from its neighbours. Pittsburgh's interval is about plus or minus 500 at an RSE of 3.4%. Salt Lake City's is about plus or minus 1,440 at 5.1%. RSE depends on how the survey sampled the metro, not on how big the metro is, so a small metro can be pinned down more tightly than a mid-size one.
How to reconcile profile counts against the official baseline
Divide your filtered profile count for a metro by the OEWS employment figure for the same metro and code. The result is a coverage ratio, and it is what lets a profile count stand next to an official number. Without it, a profile count has no denominator, and the first person who asks where it came from will knock it down.
Here is the reconciliation at national level. In Refolk's index of professional profiles, 420,743 US profiles are titled Software Engineer or Software Developer. The national OEWS figure for 15-1252 is 1,654,440, taken from a secondary source reporting the May 2024 release. That makes the ratio 25.4%: multiplying an index count by roughly 3.9 puts it on the official scale.
What the ratio proves, and when it lies
The ratio shows that your profile filter and the official series are measuring something of the same size, and it gives you a documented conversion between them. It misleads in three ways:
- Job title is not the same as occupation. "Software Engineer" profiles include people OEWS would classify under 15-1253 and 15-1254, and the ratio absorbs that error without showing it. Rerun the count with a skill filter added. If the ratio moves a lot, the title filter was picking up other occupations.
- Coverage may differ across metros. A sample of 25 index records leaned toward San Francisco and Seattle, which may mean coverage runs higher in tech hubs. That evidence is weak and unverified, but it is reason enough not to assume the national 25.4% holds in every metro.
- Different data years. The metro figures here come from May 2023, while the national figure is May 2024 from a secondary source. On your own run, take the national and metro figures from the same release.
Wrong turn two: applying 25.4% to every metro
My first plan was to apply the national ratio to each metro and skip the per-metro division. That throws away the ratio's main job, which is to show where the index sees a metro differently. Compute the ratio separately for each metro. If one metro's ratio departs sharply from the others, that is a finding in itself. Either the index covers that metro differently or the title mix there is different. The skill-filter rerun tells you which.
This step is mostly friction: the same filter, worded identically, run five times. In Refolk, the filter is a plain-English sentence. Each run is the same sentence with the city swapped, and the count comes back with the profiles behind it so you can spot-check what the filter caught.
Software engineers and software developers living in the <metro> metro Senior software engineers in <metro> with Go and Kubernetes experience at companies with fewer than 2,000 employees Engineering managers in <metro> who have led backend teams of 5 or more at a SaaS company Directors of Engineering living in the <metro> metro who previously opened or grew a satellite office Heads of Engineering in <metro> with 5 or more years in role at companies above <minimum company size>
Replace <metro> with each shortlisted city and keep every other word identical across all five runs, so differences come from the market and not the wording.
How many local leads a 40-person hub needs, and whether they exist
Plan on 6 to 9 local lead seats: 5 to 8 engineering managers plus one site lead, rising to 9 if the managers are new to the role. Lead depth is the column that most often sinks a hub, because hiring can't move faster than the supply of people who can run teams.
The span figures come from two places. For knowledge and innovation roles, 5 to 8 direct reports per manager is typical, and top-performing teams often cluster at 6 to 7. Will Larson recommends 6 to 8 in An Elegant Puzzle. New engineering managers, with 0 to 2 years in the role, should start with 3 to 5 direct reports.
| Span | EMs | Plus site lead | Total lead seats |
|---|---|---|---|
| 5 | 8 | 1 | 9 |
| 6 | 7 | 1 | 8 |
| 7 | 6 | 1 | 7 |
| 8 | 5 | 1 | 6 |
Don't size this with averages across all functions. Gallup reported that the average number of direct reports per manager rose from 10.9 to 12.1 in a single year. That is an all-roles figure. Plug it in here and it will tell you a 40-person hub needs far fewer leads than engineering teams actually run with.
Lead depth does not track headcount
The number of people with lead titles doesn't scale with the number of individual contributors (ICs), the engineers who don't manage anyone. In Refolk's index, the country split shows it plainly:
| Country | IC titles | Lead titles | ICs per lead title |
|---|---|---|---|
| United States | 420,743 | 68,216 | 6.2 |
| Canada | 47,415 | 5,640 | 8.4 |
| Canada as % of US | 11.3% | 8.3% | n/a |
Canada holds 11.3% of the US IC count but only 8.3% of the US lead-title count, so its lead pool is 27% thinner than its headcount would predict. Lead titles cluster where companies are headquartered. A market can win on headcount and lose on lead seats, and this is the measured instance of that pattern in the data I have. Whether any of the five metros repeats it is exactly what the per-metro lead count has to answer.
Headcount versus lead depth
Wrong turn three: counting lead titles at face value
In the US sample, "Head of Engineering" outnumbered "Engineering Manager" 14 to 2. That ratio points to startup founders and one-person teams using the grandest available title, not to a deep bench of people who have run groups of six. A raw lead-title count inflates lead depth wherever small companies are common.
The fix is a hard filter. Require 5 or more years in the role, or set a minimum company size, before a profile counts toward lead depth. The most useful single query for a hub is the satellite-office one in the template above, because opening a remote site is a different job from running a team at headquarters.
There is no published ratio for how many local candidates per seat counts as enough. A count that merely equals the seat count is not depth, because you're asking people who already run teams to move jobs. Record the multiple of candidates to seats for each metro and show it on the page rather than labelling a metro "deep".
Rival demand and talent renewal: the two columns that age fastest
Count rival demand from deduplicated job postings, not from the employers listed on profiles. Count talent renewal from CIP 11 degree completions joined on metro code, plus graduate-degree in-migration. Both columns change faster than the official baseline, so date every figure on the page.
Counting rival demand
The practitioner procedure runs in a fixed order:
- Match on source job IDs and canonical URLs, which removes the same posting syndicated across sources.
- Group what remains by a normalised key of company, title, city and country.
- Review same-key groups by hand before collapsing any of them.
Step 3 exists because sources disagree on whether to collapse same-key postings. An employer hiring three data engineers in London for different teams produces three postings with the same key. One academic study treated a cosine similarity of 0.95 or higher between postings as a duplicate, and noted that international companies may post the same job across multiple regions. Indeed's own data FAQ warns that its deduplication is built for the job seeker's experience, not for accurate counts of available jobs. Aggregator totals are therefore not counts.
On the page, show three things per metro:
- the named list of distinct employers hiring the same profile
- the requisition count with same-key groups collapsed
- the requisition count uncollapsed
No public threshold separates a contestable metro from a saturated one. Don't label a metro "saturated". Show the list and let the reader see who you'd be competing with.
Wrong turn four. I first built the rival list from the employers that showed up most often on index profiles. That list came from a sample of 25 records. It describes where profiles worked, not who is hiring now, and it can't stand in for rival employers. Use the deduplicated postings list.
Measuring talent renewal
IPEDS Completions reports degrees awarded by program at the 6-digit CIP (Classification of Instructional Programs) code. Count both CIP 11.0101 and 11.0701. The CRA's Generation CS appendix explains why: some institutions report computer science completions under 11.0101 and others under 11.0701.
The IPEDS institution file carries county FIPS and a 5-digit CBSA code for where the college is located, which means the main campus if there is more than one. Join completions to metros on that CBSA code. The cori.data.ipeds package in R handles county-level IPEDS if you'd rather not build the join yourself.
For in-migration, ACS table B07009 includes the cell "Moved from different state: Graduate or professional degree" (B07009_030E), counted by where people live now. Check in the Census API that the table is available at MSA geography before you rely on it. I haven't verified that here.
Keep completions and in-migration as two separate numbers. They measure different things in different units, and adding them together hides which one is doing the work.
Step-by-step: five metros to one defended pick
The procedure runs in eight steps. Steps one to six take about five to six analyst days in total, and the ramp model and the final ranking come after.
From five shortlisted metros to a defended pick
- Fix the role mix and boundaryMap each role to an SOC code and lock all five metros to OMB 23-01 MSAs. Done when there is a role-to-SOC table and a county list per metro, in about half a day.
- Pull the official baselineTake employment, RSE and LQ from the latest OEWS metro release and compute 95% intervals. Done when every metro has an interval, not a point estimate, in about half a day.
- Count profiles and reconcileRun the same title-plus-skill filter per metro and divide by that metro's OEWS figure. Done when the coverage ratio is documented per metro, in about a day.
- Count lead depthCount local Engineering Manager, Director and Head of Engineering profiles with a tenure or company-size filter, and compare with 6 to 9 seats. About a day.
- Count rival demandDeduplicate by job ID and canonical URL, then by normalised key, review same-key groups, and list employers hiring the same profile. One to two days.
- Measure renewalJoin IPEDS CIP 11.0101 and 11.0701 completions on CBSA code and add ACS B07009 graduate in-migration where available. About a day.
- Estimate the staffing rampBuild a ramp gated by manager seats from the step 4 counts, with every assumption labelled. No public benchmark exists for this.
- Rank, pick, and name the reversing factOrder rows without ranking inside overlapping intervals, write the pick in one sentence, and name the single fact that would flip it. One afternoon.
Building the ramp without a borrowed pace
No public source documents how many engineers per month a hub hires once its local lead starts. Don't borrow a number from a vendor deck. Build the ramp from the mechanism instead. The constraint is manager seats, not recruiter capacity. Each lead you hire opens a span's worth of IC seats: 6 to 8 for an experienced manager, 3 to 5 for a first-time one. The ramp is a sequence of lead start dates, each followed by the reports that lead can absorb.
Two consequences follow:
- A metro staffed with first-time managers needs about 9 lead seats rather than 6, which pushes its full-strength date out even if its IC pool is deep.
- A metro with a thin lead count has a slow ramp no matter how many ICs live there. That is how the headcount trap shows up in the schedule.
How this analysis goes wrong
Most broken hub comparisons fail in one of seven predictable ways, and each one has a check you can run locally before the page goes to leadership. This is the most valuable section of the guide: each failure below produces a confident-looking number that is wrong.
- Title is not the same as occupation. Software Engineer profiles include people OEWS counts under 15-1253 and 15-1254, and the coverage ratio absorbs the error without showing it. Check: rerun with a skill filter and see whether the ratio moves.
- Same name, different boundary. A metro appears to shrink when its definition changed. Boston was 55,240 in May 2023 under the Boston-Cambridge-Nashua definition and about 42,310 in May 2025, a 23% drop. Whether that drop is the definition change or a real decline is not established. Check: compare county lists in the OEWS area-definitions file before comparing years.
- Ranking inside the noise. Two metros whose intervals overlap get ordered as if they differ. Check: build the interval table before ordering any rows.
- Inflated lead titles. "Head of Engineering" outnumbered "Engineering Manager" 14 to 2 in the US sample, which reflects founders and one-person teams. Check: require 5 or more years in role or a minimum company size.
- Rival demand counted twice, or merged wrongly. Postings syndicated across sources or posted in multiple regions inflate rival counts. The opposite error collapses three real requisitions into one. Check: source job ID first, normalised key second, then a manual review of same-key groups.
- Renewal counted by campus location. IPEDS assigns completions to the main campus's CBSA, so institutions that teach mostly online inflate the count for their home metro. Check: exclude institutions whose completions are mostly distance education. The IPEDS field for that is not verified here, so confirm it in the IPEDS Data Center manual.
- Top-employer lists built from a sample of 25. The employers that appear most on index profiles are not your rivals for hires. Check: use the deduplicated postings list.
A metro can win on headcount and lose on the nine people who decide whether the other thirty-one get hired.
Writing the pick, the reversing fact, and keeping the page current
End the page with two sentences: one naming the metro, and one naming the single measured fact that would flip the pick. Then re-pull the baseline whenever OEWS publishes a new metro release. The reversing fact is what makes the pick defensible. It tells leadership exactly what to watch, and it tells the next analyst exactly what to recheck.
Hub: 40-person engineering hub | Role anchor: SOC 15-1252 | Boundary: OMB 23-01 MSA Baseline release: OEWS <release> | Index pull date: <date> | Postings window: <dates> Metro | OEWS emp (95% interval) | LQ | Coverage ratio | Lead count (filtered) vs seats | Rival employers (reqs collapsed / uncollapsed) | CIP 11 completions | Grad in-migration | Ramp to 40 <metro> | <n> (<low> to <high>) | <lq> | <ratio> | <count> vs <6 to 9> | <n> (<a> / <b>) | <n> | <n or not available at MSA> | <lead-gated sequence> Ties: <metros whose intervals overlap, and which column broke the tie> Pick: <metro>, because <the one or two columns that decided it>. Reversing fact: If <single measurable fact> turns out to be <threshold you set>, the pick moves to <metro>. Known gaps: <columns not filled, and how they would be filled>
One row per metro, every cell carries its source and date; leave a cell blank and say why rather than estimating.
Where the worked example stands
On the official layer, the order is San Jose, San Francisco, Austin, Salt Lake City, Pittsburgh, and no adjacent pair overlaps. That is as far as the evidence in this pass goes at metro level. The lead-depth, rival and renewal columns couldn't be split by metro, because the index query filtered by country only. So the honest page today says this: San Jose leads on raw headcount. Its LQ of 7.75 is exactly the profile that makes rival demand the most likely reversing fact. And the Canada split shows that a headcount leader can still be thin on leads.
I would not write a final pick from the official layer alone. Writing one would repeat wrong turn one. Fill the lead and rival columns with the query set above, apply the tenure filter, and the reversing fact will usually be one of two sentences: "the tenure-filtered local lead count is below the seats this hub needs", or "the deduplicated rival list in this metro already contains the employers our candidates would leave."
Keeping it current
The page goes stale through three mechanisms, and each one has a re-check:
- New OEWS release. Re-pull employment, RSE and LQ, and recompute the intervals. If the national figure moved, recompute the coverage ratio.
- Boundary revision. Before comparing to the last version of the page, confirm each metro's county list against the current OMB bulletin and the OEWS area-definitions file.
- Postings window. Rival demand changes fastest of all. Re-run the deduplication and date the window on the page.
Before the page goes to leadership
- Every metro is defined as an OMB 23-01 MSA with its county list attached
- Every role in the mix maps to an SOC code, and the table is on file
- Each metro shows a 95% interval, and no overlapping pair is presented as ranked
- The coverage ratio is computed per metro, with a skill-filter rerun recorded
- Lead counts use a tenure or company-size filter and are shown against 6 to 9 seats
- Rival employers come from deduplicated postings, with collapsed and uncollapsed counts
- Renewal counts include both CIP 11.0101 and 11.0701, and online-heavy institutions are flagged
- The ramp is gated by manager seats and labels every assumption
- The page ends with one pick and one reversing fact that someone could measure
Once the page passes the checklist, hand it over with the reversing fact highlighted. That sentence is what keeps the recommendation honest after the meeting.
Questions practitioners ask
What does a location quotient for software developers tell me about a metro?
It tells you how concentrated software developer jobs are locally compared with the national average. An LQ above 1 means developers are a larger share of local employment than average. San Jose's 7.75 and Pittsburgh's 0.64 sit at opposite ends. A high LQ shows market depth, but it says nothing about whether you can hire there, because the same concentration usually means many established employers competing for the same people.
Can I use professional profile counts instead of OEWS employment figures?
Use them alongside OEWS, never instead of it. A profile count only becomes defensible once you divide it by the official OEWS figure for the same metro and occupation code to get a coverage ratio. Nationally, Refolk's index covers about 25.4% of the official software developer count. Coverage may differ by metro, so compute the ratio per metro rather than reusing the national one.
How many engineering managers does a 40-person engineering hub need?
Plan for 5 to 8 engineering managers plus one site lead, so 6 to 9 lead seats in total. At a span of 8 reports you need 6 seats, and at a span of 6 you need 8. If your managers are new to the role and carry only 3 to 5 reports, plan for about 9. Fill these seats first, because the hiring ramp cannot move faster than the managers in place.
What RSE is too high to rank two metros against each other?
No published threshold exists. BLS only requires an RSE under 50% for an estimate to be published. The working rule is to compute each metro's 95% interval, the estimate plus or minus 1.96 times RSE times the estimate, and treat any two metros whose intervals overlap as tied. A small metro with a low RSE can be easier to tell apart from its neighbours than a larger metro with a high RSE.
Should I compare metros by MSA, combined area, or commute radius?
Use the MSA. It is the only boundary with an official OEWS baseline, and the current estimates use the OMB Bulletin 23-01 definitions. No public source measures how a role-level count shifts between an MSA, a combined statistical area and a commute radius. You can report a radius as context, but nothing official exists to reconcile it against.
How do I count rival employers without double counting job postings?
Deduplicate in two passes. First match on source job IDs and canonical URLs, then group what remains by a normalised key of company, title, city and country. Review same-key groups by hand before collapsing them, because three real requisitions for different teams can share one key. Report the requisition count both collapsed and uncollapsed, and base the employer list on postings, not on where profiles work.
Try it on the search you came here for
Stop building boolean strings. Just describe the person.
Type one sentence. I plan the search, read GitHub, public LinkedIn and Crunchbase records, and the open web as it is right now, and hand back a ranked list with the reason next to every name.
01Describe them
One plain sentence. Role, city, stack, stage, whatever matters to you.
02I read the web live
GitHub, public LinkedIn and Crunchbase records, the open web. Not a database that went stale last quarter.
03You read the shortlist
Ranked, with the reasoning under every name. Open a profile, ask a follow-up, narrow it down.
- Staff backend engineers in NYC who shipped Rust in production
- Series A fintechs in SF under 50 people, growing headcount this year
- Maintainers of fast-growing Rust web frameworks on GitHub
- No boolean, no filters, no seat to buy. One box.
- Read at search time, so a profile updated yesterday counts today.
- Every step visible as it runs, every name with its reason.
500 free credits on sign-up. No card, no demo call. See real searches.