skip to content

Should a project publish an sdist alongside its wheels, or are wheels enough?

level: middleimportance: should knowfreq 30%

answer

  1. Ask who installs it and by which path
  2. Wheels only cover targets you built
  3. Someone always needs the source of record
  4. A broken tarball is worse than none
  5. Test the fallback path in CI

basics

~20 s

Publish both. Wheels cover the targets you actually built for; the sdist is the fallback for everything else and the source of record for auditors, distribution packagers and anyone who needs to patch or rebuild your code.

solid answer

~50 s

Wheel-only releases work right up to the moment someone is on a target you did not build for — a new interpreter release, an unusual architecture, a platform outside your matrix — and then they get a hard `no matching distribution` failure with no fallback. The sdist is also what source-based consumers need: distribution packagers and rebuild pipelines build everything from source by policy, reviewers read the source of record rather than a build output, and anyone patching your library starts from the tarball. The real cost of shipping one is that it must actually build; a broken sdist is worse than none, because installers will try it and fail late on someone else's machine. That is why a release build should produce the wheel from the sdist. For a pure-Python project the coverage argument is weak — one `py3-none-any` wheel reaches everyone — but the auditability argument still holds.

go deeper

for a junior

Know that a normal release contains both a source tarball and one or more wheels, and that the tarball is what someone installs when no wheel fits their machine.

for a middle

Argue both sides: the coverage and auditability the sdist buys, the obligation that it must actually build, and why a pure-Python project's calculation differs from a compiled one's.

for a senior

Show the verification: build the wheel from the sdist, install the tarball on a clean image in CI, and document which targets are tested rather than leaving the source path untested and implied.

for a principal

Own the distribution policy — who your consumers are, whether source builds are permitted at deploy time, what the immutability of published versions means for incident response, and when a wheel-only internal package is defensible.

### The question behind the question "Wheels only?" is really "who consumes my project, and by what path?" Wheels serve the majority path: an installer on a supported target that wants a fast, reproducible install. The sdist serves every other path, and the interview answer is about knowing what those paths are rather than reciting a rule. ### What a wheel-only release breaks **Targets outside your matrix.** A compiled project's wheels are valid only for the interpreter, ABI and platform combinations you built. Everyone else — a newly released Python minor, an architecture you do not have runners for, a platform variant with a different C library — previously had a slow-but-working source path. Take the sdist away and that becomes a flat failure to install, with a message that says nothing about the cause. **Source-based redistribution.** Operating-system distributions, scientific rebuild channels and internal mirrors that rebuild everything from source do not install your binaries; their policy is to compile from source with their own toolchain and patches. A wheel-only project cannot be packaged by them without them fetching the source from elsewhere, which turns your version-control host into an accidental part of their supply chain. **Audit and patching.** A wheel is a build output. It typically contains no tests, no build configuration, and — where the project is compiled — no source at all for the compiled part. A reviewer who wants to know what the released version actually contains, or an operator who needs a same-day fix in a dependency, needs the source that corresponds to the release, published under the same immutable version. ### What shipping an sdist costs It is not free, and pretending otherwise is the naive answer. **It must build.** The moment an sdist exists, installers may use it, and they will do so at the worst possible time — on a user's machine, in someone's deployment window. A tarball missing a header, a template, a generated version file or a build script fails there rather than in your CI. This is why a release build should build the wheel from the unpacked sdist, and why a release pipeline should have one job that installs the sdist on a clean image with only a toolchain present. **It leaks nothing you should have had.** An sdist ships your source layout and everything the backend was told to include. That is only a problem if you were relying on the wheel to omit something, which is not a security control in the first place. **It invites source builds you did not plan for.** Users on unbuilt targets will compile, and their result depends on their toolchain. You cannot prevent that by omitting the sdist — you only convert it into a failure — but you should be aware that it is a supported path and say in your documentation which targets are actually tested. ### The pure-Python case If a project has no compiled code it ships a single `py3-none-any` wheel that installs on every interpreter and platform, so the coverage argument largely evaporates: nobody is forced onto the source path by a tag miss. The remaining arguments still stand, and they are cheap to honour — the sdist costs one extra artefact in a build that already produces it by default, and it is what makes the release readable and rebuildable by people who are not you. ### The genuinely wheel-only case There is one: a private, internal distribution built by your pipeline for exactly the interpreters and images your organisation runs, consumed only from your own index. Every consumer is inside the matrix, source is available from version control to everyone entitled to it, and the deployment policy may even forbid source builds outright so that nothing is ever compiled at deploy time. Stating that case correctly — and noting that it stops being true the day an external team consumes the package — is what separates a considered answer from a memorised one. ### What to say in an interview Publish both by default; treat the sdist as a tested artefact rather than a by-product; verify it in CI by building the wheel from it and installing it on a clean image; and reserve wheel-only for a closed, internal audience whose targets you fully control. Then add the sharpest consequence: because published versions are immutable, an sdist you discover to be broken cannot be fixed in place — it costs a new version and a yank, which is a much worse afternoon than the CI job that would have caught it.

  • How would you verify in CI that your sdist is actually usable?
    Two checks. Build the wheel from the unpacked sdist rather than from the working tree, which fails immediately if a needed file is missing. Then, in a separate job on a clean image carrying only a compiler and headers, install the tarball with binaries disabled and import the package. That job exercises exactly the path your unbuilt-platform users take.
  • A user reports 'no matching distribution' for your wheel-only package. What are their options?
    None that are good, which is the point. They can pin to an older release that still had a compatible wheel, build from your version-control tree themselves and hope it matches the released version, or wait for you to publish a wheel for their target. Publishing an sdist would have made this a slow install rather than a blocked one.
  • You discover your published sdist is missing a file. Can you re-upload a fixed one?
    No. A published version is immutable on the index, so the file cannot be replaced. The fix is a new patch version with the corrected tarball, plus yanking the broken one so resolvers stop selecting it while existing pinned installs keep working.

saying these in an interview costs you the question

  • Says wheels made sdists obsolete
  • Treats the sdist as an untested by-product of the build
  • Ignores rebuilders and auditors as consumers
  • Thinks omitting the sdist prevents source builds
  • Believes a broken published artefact can be replaced in place

context