<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matthew Wimpelberg</title>
    <description>The latest articles on DEV Community by Matthew Wimpelberg (@matthew_wimpelberg_79193b).</description>
    <link>https://dev.to/matthew_wimpelberg_79193b</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3117473%2F0015b293-7100-4a57-88bb-5368b1a76d2a.jpg</url>
      <title>DEV Community: Matthew Wimpelberg</title>
      <link>https://dev.to/matthew_wimpelberg_79193b</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matthew_wimpelberg_79193b"/>
    <language>en</language>
    <item>
      <title>k6: The Tool, The Philosophy, and Your First Test</title>
      <dc:creator>Matthew Wimpelberg</dc:creator>
      <pubDate>Tue, 26 May 2026 09:37:09 +0000</pubDate>
      <link>https://dev.to/matthew_wimpelberg_79193b/k6-the-tool-the-philosophy-and-your-first-test-2ccp</link>
      <guid>https://dev.to/matthew_wimpelberg_79193b/k6-the-tool-the-philosophy-and-your-first-test-2ccp</guid>
      <description>&lt;p&gt;I've been going deep on k6, Grafana's open-source load and performance testing tool. This is the first in a four-part series documenting that journey, from first principles to a full test suite running against a live Kubernetes environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why k6?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most load testing tools treat tests as configuration. k6 treats them as code, JavaScript, version-controlled, modular, and reviewable like any other engineering artifact. That's a meaningful philosophical difference. It means your performance tests live in the same repo as your application, go through the same review process, and can be maintained by the same team.&lt;/p&gt;

&lt;p&gt;For those of us already in the Grafana ecosystem, there's another compelling reason: k6 is a Grafana Labs product. Test results stream natively into Grafana Cloud. Custom metrics you define in your scripts become queryable Prometheus time series. Your load test data lives alongside your infrastructure metrics, traces, and logs in one place with one query language and one alerting system.&lt;/p&gt;

&lt;p&gt;That unified observability story is what made me want to understand k6 deeply, not just at the surface level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What k6 covers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most people think of k6 as a load testing tool. It's actually much broader:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smoke testing: Is the app up and returning the right things?&lt;/li&gt;
&lt;li&gt;Load testing: How does it behave under realistic traffic?&lt;/li&gt;
&lt;li&gt;Stress testing: Where does it break?&lt;/li&gt;
&lt;li&gt;Soak testing: Does it degrade over hours?&lt;/li&gt;
&lt;li&gt;Browser testing: Real Chromium, Web Vitals, frontend performance&lt;/li&gt;
&lt;li&gt;Synthetic monitoring: Scheduled availability checks from global probe locations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One tool, one scripting language, the full testing lifecycle from development through production monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your first k6 script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you have k6 installed, a minimal test looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;30s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;http_req_failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rate&amp;lt;0.01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;http_req_duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p(95)&amp;lt;500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://test-api.k6.io/public/crocodiles/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status 200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;response &amp;lt; 500ms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things are happening here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;options&lt;/strong&gt; tells k6 how to run — 10 virtual users for 30 seconds, and two thresholds that define pass/fail: less than 1% of requests can fail, and the 95th percentile response time must stay under 500ms. If either threshold is violated, k6 exits with a non-zero code. Your CI pipeline fails. That's your SLO enforced automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;checks&lt;/strong&gt; are per-request assertions. A failing check doesn't stop the test, it increments a failure counter. At the end you see pass rates across all iterations, not just a binary pass/fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;sleep&lt;/strong&gt; is think time between requests. Without it, k6 hammers the server as fast as possible with unrealistic load that produces misleading results. Real users read pages. sleep(1) models that.&lt;/p&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;k6 run script.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The terminal output gives you request count, duration percentiles (p50, p90, p95, p99), error rate, data sent/received, and threshold results. A clean first run looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ status 200
✓ response &amp;lt; 500ms

http_req_duration: avg=45ms p(95)=112ms
http_req_failed:   0.00%
✓ thresholds passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's next&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My next post will cover building a real test suite with a shared library architecture, smoke testing against a live microservices app running on a homelab Kubernetes cluster, and what happens when your first run doesn't go as expected.&lt;/p&gt;

&lt;p&gt;The target app is Google's Online Boutique.  It's a realistic e-commerce microservices demo with 11 services.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;#k6 #Grafana #LoadTesting #PerformanceTesting #Observability #SRE #Kubernetes&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
