In k6, what does an import from the k6/x/ namespace imply for the binary your team and CI must run?
answer
- the prefix marks an extension
- compiled in, not dropped in
- the artefact is a binary build
- stock release will not load it
- who owns the build pipeline
basics
~20 sThe script cannot run on a stock k6 release. Extensions are compiled into the binary under the k6/x/ prefix, so what the team pins and distributes is a specific k6 build, not just a script and a version.
solid answer
~50 sk6's built-in module set is fixed inside the binary. Extensions live under the reserved **`k6/x/`** prefix, and an extension is compiled in rather than loaded from a file next to the script — so the moment one scenario imports `k6/x/<something>`, "install k6" stops being enough. The thing your pipeline pins becomes a *binary build*: an executable that must exist on every CI runner and every laptop, kept in step with the k6 version the team otherwise uses. The prefix is the visible marker: a reviewer seeing it in an import line knows this script will not load on a stock release. In return you get a single self-contained executable, with no plugin resolution while the test runs and no add-on drifting out of step with the core. What you pay is owning a build and distribution step for the tool itself.
go deeper
Recognise the k6/x/ prefix. It marks an extension module, and an extension is compiled into a k6 binary rather than installed alongside the script.
Explain the consequence: a script importing k6/x/ needs a binary built with that extension, so the same script can load fine locally and fail immediately on a runner with a stock release.
Talk about ownership and distribution — who produces the build, how it reaches every runner and laptop, and how upgrades stay in step when the extension and core ship as one executable.
Frame it as a build-versus-packaging trade. k6 gives you one self-contained executable at the price of owning a build pipeline for your test tool; decide whether that ongoing cost is one your organisation can carry.
## The `k6/x/` namespace k6 reserves the module prefix **`k6/x/`** for extensions. Everything else k6 can import — its HTTP module, its metrics module, its execution module — is built into the binary that Grafana publishes. An extension is Go code that is **compiled into a k6 binary**; it is not a file dropped beside the script, and it is not fetched at run time by the script's import statement. That design shows up in a real v2 change: `k6/experimental/redis` was removed, and importing it now throws with a message pointing at **`k6/x/redis`**. Functionality that used to ship in the binary moved behind the extension prefix, which is a concrete reminder that the prefix, not the feature, is what tells you where the code lives. ## What that changes about adoption - **The unit you pin is an executable.** Without extensions, a team pins a k6 version and a script. With one, it pins a *build* — a particular binary containing a particular set of extensions. - **The stock release and the stock container image do not have it.** Whatever you install from a package manager or pull as the published image will fail to load a script that imports a module it was not built with. - **Laptops and CI must agree.** If an engineer's binary has the extension and the runner's does not, the script passes locally and fails to load in the pipeline, with an error about the import rather than about the system under test. - **Version skew becomes your problem.** The extension and the k6 core are compiled together, so upgrading k6 means producing a new build rather than upgrading one component. - **The prefix is a review signal.** `k6/x/` in an import line is the one-glance answer to "can this run anywhere?" — worth calling out in review, because nothing else in the script announces it. ## What the model buys - **One self-contained artefact.** The thing you ship is an executable. There is no runtime plugin path to configure, nothing to resolve while a test is running, and no partially-installed add-on to diagnose at three in the morning. - **No run-time version negotiation.** Because the extension and the core were built together, the combination that passed your tests is the combination that runs. - **A hard boundary.** You cannot accidentally acquire an extension; either it was compiled in or the import fails immediately, before any load is generated. ## The trade-off in one table | question | k6 with only built-in modules | k6 with a `k6/x/` import | |---|---|---| | what CI installs | a published k6 release | a specific build your team produces or sources | | what you version | the k6 version | the k6 version **and** the extension set | | when a missing capability is discovered | never — imports resolve | at load time, before load is generated | | upgrade unit | one release | a rebuild of the combined binary | | failure mode | none of this class | works locally, fails to load on a runner | ## How this weighs in a tool choice For a team choosing a generator, the question is not whether the extension mechanism is good — it is whether you will need it. Ask two things, in this order: 1. **Does the stock binary already speak your protocols?** If your traffic is HTTP, gRPC, WebSocket or browser, the built-in modules cover it and none of this cost applies. Most teams never leave the stock binary. 2. **If not, who owns the build?** Producing and publishing the binary is a real, recurring engineering task. If nobody owns it, the extension is a future outage in the pipeline rather than a capability. A tool whose add-ons are drop-in files on a runtime path turns "add a plugin" into a packaging decision; k6 turns it into a build decision. Neither is wrong, but they land on different teams. The packaging model is cheap to start and gives you a runtime that can be misassembled; the build model costs you a pipeline and gives you an executable that either works everywhere or fails identically everywhere. Knowing which of those your organisation is better at is most of the answer.
- A k6 script that runs on an engineer's machine fails immediately on the CI runner. What do you suspect first?That the script imports a module under `k6/x/` and the runner has a stock binary. The failure appears at load time, complaining about the import rather than about the target system, and it is resolved by making the runner use the same build — not by changing the script.
- Does using an extension change how you version the test?Yes. The script's k6 version is no longer a complete description of what ran, because the extension set is part of the executable. The reproducible identity of a run becomes the binary build, so that is what release notes and pipeline configuration should name.
It is the difference between a phone whose storage is chosen at purchase and one with a card slot. The sealed device is simpler to ship and support, but adding capacity means buying a different device.
saying these in an interview costs you the question
- Believing a k6 extension is a file dropped next to the script
- Assuming the published k6 release can load any k6/x/ module
- Thinking k6 downloads extension modules from a registry at run time
- Treating the k6 version alone as a full description of what ran
- Adopting an extension without deciding who owns the binary build