Hire Python developers
Python spans web back ends, data work and machine learning, so hiring for it starts with deciding which of those three jobs you actually mean.
What Python actually is
Python is a general-purpose language maintained by the Python Software Foundation, with a release each year and a clear support window for each version. It is used across an unusually wide range of work: web services, data pipelines, machine learning, scientific computing, automation and internal tooling. That breadth is its main commercial advantage and the main source of confusion when hiring.
A Python developer building a Django application and a Python developer training models share a language and very little else. The first works with request lifecycles, database transactions, migrations and authentication. The second works with dataframes, notebooks, numerical libraries and experiment tracking. Both are legitimately Python roles, and a process that does not distinguish them produces a shortlist where most candidates are wrong for reasons nobody wrote down.
The language itself rewards clarity and punishes cleverness, which is why it is often the language a team with mixed backgrounds converges on. It is also slower than compiled alternatives for computation, which matters far less than people expect in web work, where time is spent waiting on databases, and far more than people expect in data work, where the answer is to push the computation into libraries that are not written in Python.
The part that separates seniors from mid-levels
The thing that catches people out is that CPython runs one thread of Python bytecode at a time. Threads are useful for waiting on input and output and give you nothing for computation. Parallel computation means multiple processes, or work pushed into libraries that release the lock and run native code underneath. A developer who understands this designs around it; one who does not writes a thread pool for a computational task and cannot explain why it is no faster.
The second area is asynchronous Python, which is now mainstream in web work and is a genuinely different programming model rather than a syntax variation. Mixing blocking calls into an async application silently ruins its concurrency, and it is an easy mistake because the code looks correct and works under light load. Candidates who have run an async service in production can usually name the blocking call they found.
The third is packaging and environments, which sounds like plumbing and is the most common cause of a Python project being hard to work on. Dependency resolution, lockfiles, virtual environments and reproducible builds are where Python has historically been weakest and where tooling has changed fastest. Someone whose habits were formed years ago may still be doing this in a way that no longer matches how the ecosystem works.
Where Python is used
The label “Python developer” covers several jobs that share a technology and little else. These are the settings the work usually turns up in, and the one you are hiring into should shape the whole process, because the judgement each demands is different.
Web back ends
Django and FastAPI services behind product applications, often where the same team also does data work.
Data pipelines
Scheduled extraction, transformation and loading between systems, usually orchestrated and monitored rather than run by hand.
Machine learning
Training, evaluation and serving of models, where Python is the interface to libraries written in other languages.
Scientific and quantitative work
Simulation, analysis and modelling in research, finance and engineering, often in notebooks before it is anywhere else.
Automation and internal tooling
Scripts and services that move data between systems, frequently the least documented and most depended-upon code in a company.
Infrastructure tooling
Deployment, configuration and operational tooling, where Python is the glue between cloud services.
If a candidate's experience sits in a different row of that list from the work you have, that is not a reason to reject them, but it is the thing to probe. Ask what would be different about their approach in your setting. Someone who can answer that has transferable judgement. Someone who says it would be much the same has probably not thought about it.
Which Python versions are still supported
Python publishes a clear support window for each release, which makes the supported set unusually unambiguous compared with most ecosystems.
The table is generated from public release data rather than written by hand, so it states what is supported today rather than what was true when any given article was published. Of the 8 most recent release lines, 5 are still maintained and 3 have passed their published end-of-life date. That distinction is the practical one when you read a job specification: a requirement written against a line that is now out of support tells you the specification is older than the codebase it describes, and it is worth asking which of the two the new developer will actually work in.
| Release | Released | End of life | Status | Latest patch |
|---|---|---|---|---|
| 3.14 | 2025-10-07 | 2030-10-31 | Maintained | 3.14.7 (2026-08-05) |
| 3.13 | 2024-10-07 | 2029-10-31 | Maintained | 3.13.15 (2026-08-05) |
| 3.12 | 2023-10-02 | 2028-10-31 | Maintained | 3.12.14 (2026-08-12) |
| 3.11 | 2022-10-24 | 2027-10-31 | Maintained | 3.11.16 (2026-08-13) |
| 3.10 | 2021-10-04 | 2026-10-31 | Maintained | 3.10.21 (2026-08-13) |
| 3.9 | 2020-10-05 | 2025-10-31 | End of life | 3.9.25 (2025-10-31) |
| 3.8 | 2019-10-14 | 2024-10-07 | End of life | 3.8.20 (2024-09-06) |
| 3.7 | 2018-06-27 | 2023-06-27 | End of life | 3.7.17 (2023-06-05) |
Source: endoflife.date public release data, read 2026-09-25.
Two questions follow from this table in an interview. The first is which line the candidate has most recently shipped against, because someone whose last production work was on an unsupported line has not had to deal with the changes since. The second is how they have handled an upgrade. Version migrations are where you see whether a developer reads release notes, writes characterisation tests before changing anything, and knows how to stage a rollout, or whether they upgrade in place on a Friday and hope.
The toolchain around it
Nobody hires for Python alone. The surrounding tools are where most of the day-to-day work happens, and a gap in any of them costs more time than a gap in the core library. This is the set that turns up most often on real job specifications alongside it.
- Django or FastAPI
- Web frameworks. Django brings an admin, an ORM and conventions; FastAPI brings typed, async-first APIs with generated documentation.
- PostgreSQL
- The usual data store, accessed through the Django ORM or SQLAlchemy.
- Pydantic
- Validation and settings, and the type layer that makes FastAPI work the way it does.
- uv or Poetry
- Dependency resolution and reproducible environments, an area where practice has changed significantly.
- pytest
- Testing, with fixtures and parameterisation used well or not at all.
- Celery or a managed queue
- Background work moved off the request path.
- pandas and Polars
- Dataframe work where the job is data rather than web.
- Ruff and mypy
- Linting, formatting and optional static typing, now standard in well-run codebases.
Related skills that frequently appear on the same specification: Machine Learning, Django, SQL, Data Engineering, AWS.
What to test in an interview
These are the topics that separate candidates in practice. Each one is given with why it discriminates, what a strong answer sounds like, and the response that should make you slow down. None of them requires a whiteboard.
Which Python job they have actually done
The single most useful first question, because the three main branches barely overlap.
- Strong answer: Describes their work concretely enough that you can place it, and is honest about which parts they have not done.
- Warning sign: Presents web, data and machine learning experience as interchangeable.
The global interpreter lock and parallelism
Decides whether someone can design work that actually goes faster.
- Strong answer: Uses processes or native libraries for computation, threads for waiting, and can explain the distinction without prompting.
- Warning sign: Proposes threads for a CPU-bound task and cannot say why it would not help.
Async Python in production
Now common, and easy to get subtly wrong.
- Strong answer: Knows what a blocking call does inside an async handler, and can describe finding one.
- Warning sign: Has written async code but never thought about what the libraries underneath are doing.
Dependency management and reproducibility
The most common reason a Python project is painful to work on.
- Strong answer: Uses lockfiles, pins deliberately, and can explain how a colleague gets an identical environment.
- Warning sign: Relies on an unpinned requirements file and has been bitten without changing approach.
Database access and the N plus one problem
The most common performance fault in Django and SQLAlchemy codebases.
- Strong answer: Knows how to inspect generated SQL, uses eager loading deliberately, and has fixed a case.
- Warning sign: Has never looked at the queries an ORM produces.
Testing approach
Python makes mocking easy, which makes it easy to write tests that assert nothing.
- Strong answer: Tests behaviour, uses a real database for integration tests, and can say what they deliberately do not test.
- Warning sign: Mocks the database and believes that constitutes coverage.
Notebook to production
Especially relevant where data and engineering meet, and a frequent source of friction.
- Strong answer: Treats notebooks as exploration and rewrites for production with tests and structure.
- Warning sign: Ships notebook code, or dismisses notebooks entirely rather than using them for what they are good at.
Warning signs in a Python codebase
The fastest way to read a candidate is to ask what they have found wrong in code they inherited. These are the patterns that come up most often, what they cost, and what fixing them looks like. A developer who recognises three or four of these from their own experience is worth more than one who can recite the documentation.
Threads for computation
- What you see: A thread pool wrapped around CPU-bound work.
- What it costs: No speed-up, added complexity, and harder debugging for nothing.
- The fix: Use processes, or push the computation into a library that releases the interpreter lock.
Blocking calls in async handlers
- What you see: A synchronous database driver or HTTP client inside an async application.
- What it costs: Concurrency collapses to one request at a time under load, while looking fine in development.
- The fix: Use async-native clients, or run blocking work in a thread pool explicitly.
Unpinned dependencies
- What you see: A requirements file with loose or absent version constraints.
- What it costs: Builds that differ between machines and over time, producing failures nobody can reproduce.
- The fix: Use a lockfile and a tool that resolves the full graph. Update deliberately rather than accidentally.
The N plus one query
- What you see: A loop over records that touches a relation on each iteration.
- What it costs: Hundreds of queries where two would do, and latency that grows with data.
- The fix: Eager load the relation. Log generated SQL in development so the pattern is visible when written.
Mutable default arguments
- What you see: A function with a list or dictionary as a default value.
- What it costs: State that persists between calls, producing bugs that look impossible.
- The fix: Default to None and create inside. Linters catch this, so its presence says something about the setup.
Notebook code in production
- What you see: Analysis code moved straight into a pipeline with cell ordering preserved.
- What it costs: Hidden state, no tests, and failures that cannot be reproduced outside the notebook.
- The fix: Rewrite into modules with tests. Keep the notebook for exploration, which is what it is good at.
What each level can own
Job titles are not comparable between companies, so it is more useful to describe levels by what a person can be left to own without supervision. These are the boundaries we use when we assess a Python developer.
- Junior
- Writes views, models or scripts inside an existing structure. Needs review on query patterns and on environment reproducibility.
- Mid-level
- Owns a service or pipeline end to end including tests and deployment. Understands where the interpreter lock matters.
- Senior
- Owns architecture in one of the branches, whether that is service boundaries and data access, or pipeline reliability and data contracts. Sets the dependency and typing policy.
- Staff
- Owns cross-cutting standards: packaging, typing, the boundary between research and production code, and the runtime upgrade path.
How the work is usually scoped
Team shape follows the kind of work, not the headcount you happen to have budget for. These are the shapes that come up most often and the constraint that actually governs each one.
Django application
- Usual team: Two developers, senior-led initially.
- What governs it: The data model is the project. Changing it after significant data exists is the expensive part.
FastAPI service
- Usual team: One to two developers.
- What governs it: Scoped by integrations rather than endpoints. Async correctness needs a reviewer who has done it.
Data pipeline build
- Usual team: One or two data engineers plus an owner for each source system.
- What governs it: Governed by source data quality, which is almost always worse than described.
Research to production
- Usual team: One engineer paired with the person who wrote the original.
- What governs it: Scoped by how much hidden state the original carries. Notebooks with manual steps take far longer than they look.
Version and dependency upgrade
- Usual team: One developer.
- What governs it: Depends on how far behind the codebase is and whether tests exist. Past end of life with no tests is a project.
Migration work you may actually be hiring for
A large share of Python work is not new development. It is moving an existing system from one state to another while it stays in service. These are the migrations that come up most often, and each one asks for a different kind of experience from the person you hire.
An unsupported Python version to a supported one
- Why teams do it: Security patches, and access to libraries that have dropped older versions.
- What to watch: The language changes are usually minor; the dependencies are the work. Upgrade in a branch with the full test suite running, and expect one or two packages to need replacing outright.
Synchronous Django or Flask to async FastAPI
- Why teams do it: Throughput on input-and-output-bound workloads and typed request and response models.
- What to watch: This is a rewrite of the request layer, not a port. It only pays where concurrency is genuinely the constraint; for most CRUD applications it is not, and Django's completeness is worth more.
requirements.txt to a lockfile-based tool
- Why teams do it: Reproducible builds and an end to environment drift between developers and production.
- What to watch: Resolve and pin the current state first, then upgrade. Doing both at once means any breakage has two possible causes.
Notebooks to tested modules
- Why teams do it: Anything running on a schedule or serving users needs to be reproducible and debuggable by someone other than its author.
- What to watch: Hidden state from out-of-order execution is the usual trap. Rewrite rather than export, and check that the rewritten version reproduces the original numbers before anything is switched over.
Migration work rewards a different temperament from greenfield work. The useful question in an interview is not whether someone has done the specific migration you face, but whether they have ever run one incrementally: behind a flag, with both paths live, and with a way back. Developers who have only done big-bang cutovers tend to propose them again.
What a good brief for this role contains
Most of the time lost in hiring a Python developer is lost before anyone is interviewed, in the gap between what the brief says and what the team actually needs. These are the points that, for this technology specifically, change who the right candidate is. A brief that answers them can be matched in days. One that does not produces a shortlist that looks reasonable and converts badly.
- Which branch of Python this is: web service, data pipeline, or machine learning. Everything else follows from this.
- Which framework and ORM are in use, or whether that choice is part of the work.
- Whether the codebase is synchronous or async, since async experience is a distinct skill.
- Which Python version is targeted and whether an upgrade is in scope.
- How dependencies are currently managed, because inheriting an unpinned project is a different job from joining a reproducible one.
- Whether the person will own operations as well as development, including scheduled jobs and their failures.
If you cannot answer some of these yet, that is normal and it is still worth writing down which ones are open. An unknown that is named can be worked around. An unknown that is papered over in a job specification turns into a rejected shortlist and a restart four weeks later.
What the US market pays for this work
Python work is counted by the US Bureau of Labor Statistics under Software Developers. That classification is broader than the technology itself, so treat the figures as the shape of the market a Python developer is hired into rather than as a rate card for the skill. Across the United States the Bureau counts 1,687,890 people in this occupation, with a median annual wage of $135,980.
The spread matters more than the midpoint. The 90th percentile is about 2.6 times the 10th, which is a wide band for a single occupation and tells you that the title on its own carries very little pricing information. Two people described as a Python developer can sit at $82,460 and $214,670 in the same national dataset. When a budget is set from a median without asking which end of that range the work actually needs, the hire that follows is usually the wrong one in one direction or the other.
Related classifications are worth reading alongside it, because teams hiring for Python frequently end up recruiting against these titles too:
| Occupation | Employed | 25th percentile | Median | 75th percentile | 90th percentile |
|---|---|---|---|---|---|
| Software Developers | 1,687,890 | $105,210 | $135,980 | $171,980 | $214,670 |
| Data Scientists | 262,440 | $85,660 | $120,230 | $158,880 | $199,130 |
| Computer Programmers | 92,230 | $75,850 | $100,390 | $130,680 | $160,460 |
Source: BLS Occupational Employment and Wage Statistics, May 2025. Figures cover all US employers and are not FuturByte rates.
These are employer-side wage figures for people on a US payroll. They exclude employer taxes, benefits, recruitment cost and the months a seat sits empty, all of which are real and none of which appear in a salary line. The useful way to read the table is as the cost of the alternative you are comparing against, not as a number to match.
How US metro markets compare for this role
The same job is priced very differently across the country. Ranked by median annual wage for Software Developers, the gap between the highest and lowest of the 28 metro areas covered here is a factor of about 1.7. San Jose sits at the top with a median of $213,110; Pittsburgh sits at the bottom with $124,500. A budget built from a national median will be wrong in both of those markets, in opposite directions.
| Metro area | Employed | Median wage | vs US median | Location quotient |
|---|---|---|---|---|
| San Jose, CA | 87,350 | $213,110 | +57% | 7.09 |
| San Francisco, CA | 69,030 | $186,640 | +37% | 2.68 |
| Seattle, WA | 92,770 | $167,280 | +23% | 4.10 |
| New York, NY | 121,000 | $166,830 | +23% | 1.17 |
| Boston, MA | 42,310 | $166,090 | +22% | 1.44 |
| San Diego, CA | 20,610 | $163,270 | +20% | 1.23 |
| Los Angeles, CA | 55,540 | $160,920 | +18% | 0.82 |
| Portland, OR | 18,260 | $156,000 | +15% | 1.39 |
| Washington, D.C. | 69,060 | $154,930 | +14% | 2.03 |
| Baltimore, MD | 16,850 | $138,900 | +2% | 1.14 |
| Denver, CO | 27,010 | $137,610 | +1% | 1.55 |
| Charlotte, NC | 20,820 | $135,920 | 0% | 1.41 |
| Chicago, IL | 40,370 | $134,380 | -1% | 0.82 |
| Austin, TX | 31,960 | $134,120 | -1% | 2.28 |
| Dallas-Fort Worth, TX | 67,030 | $133,290 | -2% | 1.52 |
| Philadelphia, PA | 28,480 | $133,040 | -2% | 0.91 |
| Atlanta, GA | 36,300 | $132,960 | -2% | 1.16 |
| Raleigh, NC | 12,580 | $132,770 | -2% | 1.56 |
| Miami, FL | 18,900 | $132,650 | -2% | 0.62 |
| Phoenix, AZ | 29,380 | $131,750 | -3% | 1.14 |
| Minneapolis-St. Paul, MN | 27,410 | $130,920 | -4% | 1.29 |
| Detroit, MI | 24,870 | $130,760 | -4% | 1.20 |
| Tampa, FL | 14,230 | $130,450 | -4% | 0.91 |
| Orlando, FL | 13,440 | $129,620 | -5% | 0.88 |
| Salt Lake City, UT | 19,040 | $129,600 | -5% | 2.12 |
| Houston, TX | 22,940 | $129,440 | -5% | 0.64 |
| Kansas City, MO | 12,160 | $124,990 | -8% | 1.02 |
| Pittsburgh, PA | 10,320 | $124,500 | -8% | 0.85 |
Location quotient compares how concentrated this occupation is in the metro against the national average. A value above 1 means the metro has more of this work than its size would predict.
The location quotient column is the more useful one for hiring. A high median tells you what a role costs; a high quotient tells you whether the people exist. San Jose, San Francisco, Seattle, Washington, D.C., Denver, Austin each have a quotient of 1.5 or above, meaning the work is concentrated there well beyond what the size of the local economy would predict. Those are the markets where a search is likely to be quick and competitive at the same time, and where a counter-offer is most likely to take a candidate off the table late in the process.
The opposite case is worth planning for too. In a metro with a low quotient, the total pool is small even when wages look reasonable, so the realistic options are to widen the search radius, accept a longer time to hire, or bring the capability in from outside the local market entirely. That last option is what most teams are weighing when they come to us.
Hiring risks worth naming
Every one of these has produced a bad hire somewhere. They are written down so that the process tests for them deliberately rather than discovering them in month three.
Hiring across the wrong branch. Decide whether you need web, data or machine learning before writing the brief. This is the most common Python hiring failure and it is entirely self-inflicted.
Script habits in service code. Ask how they structure something meant to run for years. Automation experience does not imply service experience.
Outdated packaging practice. Ask how a colleague reproduces their environment. This area has changed fast and habits lag.
No production experience of async. Only matters if you run async services, and matters a great deal if you do.
Hiring Python developers by metro area
Wages for this occupation vary more between US metro areas than most budget models assume. Each page below sets out the published employment and wage figures for that market, how it compares with the national picture, and what the local industry mix means for the kind of Python developer who will be available.
- New York, NY $166,830 median
- Seattle, WA $167,280 median
- San Jose, CA $213,110 median
- Washington, D.C. $154,930 median
- San Francisco, CA $186,640 median
- Dallas-Fort Worth, TX $133,290 median
- Los Angeles, CA $160,920 median
- Boston, MA $166,090 median
- Chicago, IL $134,380 median
- Atlanta, GA $132,960 median
- Austin, TX $134,120 median
- Phoenix, AZ $131,750 median
- Philadelphia, PA $133,040 median
- Minneapolis-St. Paul, MN $130,920 median
- Denver, CO $137,610 median
- Detroit, MI $130,760 median
- Houston, TX $129,440 median
- Charlotte, NC $135,920 median
- San Diego, CA $163,270 median
- Salt Lake City, UT $129,600 median
- Miami, FL $132,650 median
- Portland, OR $156,000 median
- Baltimore, MD $138,900 median
- Tampa, FL $130,450 median
- Orlando, FL $129,620 median
- Raleigh, NC $132,770 median
- Kansas City, MO $124,990 median
- Pittsburgh, PA $124,500 median
Frequently asked questions
Is Python fast enough for our back end?
For typical web work, yes, because the time goes on waiting for databases and other services rather than on executing Python. Where it is genuinely too slow is tight computational loops, and the standard answer there is to push that work into a library implemented in a faster language, which is how most performance-sensitive Python already works.
Should we use Django or FastAPI?
Django when you want batteries included: an admin interface, an ORM, authentication and migrations that all fit together, which suits product applications with a lot of CRUD. FastAPI when you are building typed APIs, want async throughout, or need generated API documentation. Both are solid. Django is usually the faster path to a working product; FastAPI is usually the better fit for a service in a larger system.
Can a data scientist write our production Python?
Sometimes, and it should be checked rather than assumed. The gap is not language knowledge but engineering practice: tests, error handling, packaging, and writing code that someone else can operate at three in the morning. Pairing a strong data scientist with an engineer for the first project is usually more effective than expecting either to cover the whole range.
Which Python version should we target?
A version still within its support window, which the release table on this page reflects from public data. Running past end of life means no security fixes. Upgrading Python is usually much less painful than teams fear, and the pain is nearly always in dependencies rather than in the language.
Why is our Django application slow?
In most cases we look at, it is the N plus one query pattern: a loop that triggers a database round trip per item. It is invisible in development with ten records and crippling in production with ten thousand. The fix is usually a few lines of eager loading, and the durable fix is logging generated SQL in development so the pattern is obvious when it is written.
Do we need type annotations in Python?
On anything that will be maintained for years, they earn their cost, particularly at function boundaries and in data structures that cross modules. They are optional and not enforced at runtime, so they are documentation the compiler can check rather than a guarantee. Most well-run commercial Python codebases now use them with a static checker in continuous integration.
How do we stop environment problems?
Use a tool that produces a lockfile resolving the entire dependency graph, commit it, and build from it everywhere including production. Most reproducibility complaints in Python trace back to an unpinned requirements file and a virtual environment created by hand. This part of the ecosystem has improved considerably and many teams are still using older habits.
Is it reasonable for one person to cover web and data work?
For a small company, often yes, and Python is one of the few languages where that is practical. The risk is depth: someone spread across both tends to be mid-level in each rather than senior in either. That is frequently the right trade early on and the wrong one once either side becomes load-bearing.