<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>kapptec</title><id>https://www.kapp.technology</id><updated>2026-05-14T00:00:00+00:00</updated><author><name>kapptec</name></author><link href="https://www.kapp.technology/en/atom.xml" rel="self"/><link href="https://www.kapp.technology" rel="alternate"/><entry><title>How Bun Got Ported to Rust — and What It Teaches Us About Working with Agents</title><id>https://www.kapp.technology/en/blog/how-bun-got-ported-to-rust</id><updated>2026-05-14T00:00:00+00:00</updated><link href="https://www.kapp.technology/en/blog/how-bun-got-ported-to-rust" rel="alternate"/><published>2026-05-14T00:00:00+00:00</published><summary>Bun&apos;s Zig-to-Rust port shipped in one 2,188-file PR. The Claude agent workflows behind it leaked through git — ten lessons worth stealing.</summary><content type="html">&lt;p&gt;&lt;em&gt;&lt;strong&gt;After the agent reported &lt;code&gt;background command &quot;Build Bun debug binary&quot; completed (exit code 0)&lt;/code&gt; and I had a Bun binary compiled from Rust, I handed Claude the next task: analyze &lt;a href=&quot;https://github.com/oven-sh/bun/pull/30412&quot;&gt;this PR&lt;/a&gt; and write a blog post on how &lt;a href=&quot;https://x.com/jarredsumner&quot;&gt;Jarred Sumner&lt;/a&gt; pulled off the Zig-to-Rust port — so the rest of us can learn to work better with agents. What follows is what came back, possibly with traces of the Mythos model.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On May 14, 2026, Jarred Sumner merged &lt;a href=&quot;https://github.com/oven-sh/bun/pull/30412&quot;&gt;PR #30412 — “Rewrite Bun in Rust”&lt;/a&gt; into &lt;code&gt;oven-sh/bun&lt;/code&gt;. It was one of the largest single PRs in the history of public open source: &lt;strong&gt;2,188 files changed, 1,009,257 lines added&lt;/strong&gt;. It reimplemented an entire JavaScript runtime — bundler, package manager, HTTP/2/3 stack, dev server, transpiler, the lot — from Zig to Rust, while keeping the same architecture and the same test suite passing.&lt;/p&gt;
&lt;p&gt;It almost certainly couldn’t have happened without Claude agents. The PR doesn’t say so out loud — the body just says “compiler-assisted tools for catching &amp;amp; preventing memory bugs” — but the commit history leaks the orchestration. Ten minutes after the merge, Jarred pushed another commit titled “Delete stray files” that removed 34 files from &lt;code&gt;.claude/workflows/&lt;/code&gt;. Those files, briefly public in git history, tell the story.&lt;/p&gt;
&lt;p&gt;This post is about what we can learn from that story — not about Bun specifically, but about how to set up a codebase, instructions, and tooling so that agents can do work humans alone couldn’t ship.&lt;/p&gt;
&lt;h2&gt;The leaked evidence&lt;/h2&gt;
&lt;p&gt;The deleted directory contained workflow scripts named in a deliberate phase order:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;phase-a-port.workflow.js
phase-b0-cyclebreak / movein / moveout / verify.workflow.js
phase-b1-tier.workflow.js
phase-b2-cycle / fill / fix-bugs / keystone / ungate-tier / verify.workflow.js
phase-c-panic-swarm.workflow.js
phase-d-build-queue / crate-shard / todo-sweep / unsafe-audit /
       blocked-on-resolve / bundler-perfile / bundler-shard /
       recursive-ungate / subtree-batch.workflow.js
phase-e-body-port / mass-ungate / proper-port / test-bringup /
       scopeguard-sweep.workflow.js
phase-f-probe-swarm / test-swarm.workflow.js
phase-g-mega-swarm / test-swarm.workflow.js
phase-h-ci-tasks / dedup / deep-dive / diff-review / idioms-audit /
       libuv-audit / main-parity / unsafe-wrap.workflow.js
lifetime-classify.workflow.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And one more file, &lt;code&gt;.apply-lock-guard.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash
# Serialize Apply-phase agents on /root/bun-5 working tree.
# Usage: flock /root/bun-5/.apply-lock bash .apply-lock-guard.sh -- &amp;lt;command...&amp;gt;
exec &quot;$@&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A four-line &lt;code&gt;flock&lt;/code&gt; wrapper to serialize parallel agents writing to a shared working tree at &lt;code&gt;/root/bun-5&lt;/code&gt;. Whoever was running this had many agents simultaneously editing the same checkout and used filesystem locks to keep them from colliding.&lt;/p&gt;
&lt;p&gt;The body of &lt;code&gt;phase-a-port.workflow.js&lt;/code&gt; is more telling:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export const meta = {
  name: &quot;phase-a-port&quot;,
  description: &quot;Phase A: draft .rs for a batch of .zig files (implement → verify → fix)&quot;,
  phases: [
    { title: &quot;Implement&quot;, detail: &quot;one agent per .zig writes draft .rs per PORTING.md&quot; },
    { title: &quot;Verify&quot;,    detail: &quot;adversarial check of .rs against .zig + PORTING.md rules&quot; },
    { title: &quot;Fix&quot;,       detail: &quot;apply verifier findings to .rs&quot; },
  ],
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One agent per Zig file. A second adversarial agent verifies against a &lt;code&gt;PORTING.md&lt;/code&gt; style guide. A third applies the verifier’s findings. All outputs are JSON-Schema-validated (&lt;code&gt;IMPL_SCHEMA&lt;/code&gt;, &lt;code&gt;VERIFY_SCHEMA&lt;/code&gt; are defined further down) so the next phase can consume them mechanically.&lt;/p&gt;
&lt;h2&gt;What’s still visible in the tree&lt;/h2&gt;
&lt;p&gt;The workflows got deleted, but their fingerprints are everywhere:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;1,292 &lt;code&gt;.zig&lt;/code&gt; files retained alongside 1,432 &lt;code&gt;.rs&lt;/code&gt; files.&lt;/strong&gt; The root &lt;code&gt;CLAUDE.md&lt;/code&gt; is explicit (line 141): &lt;em&gt;“These are the original Zig implementation, kept only as a porting reference — they are not compiled and not shipped. New code goes in &lt;code&gt;.rs&lt;/code&gt;. When fixing a bug or porting a behavior, the &lt;code&gt;.zig&lt;/code&gt; sibling is the source of truth for intended semantics.”&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;10,841 &lt;code&gt;PORT NOTE:&lt;/code&gt; / &lt;code&gt;PERF(port):&lt;/code&gt; / &lt;code&gt;TODO(port):&lt;/code&gt; comments&lt;/strong&gt; across the Rust source. That’s an average of seven to eight per file. They aren’t decorative. Look at &lt;code&gt;src/bundler/defines.rs&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-rust&quot;&gt;// PORT NOTE: Zig pre-counted `key_buf_len`/`e_strings_to_allocate` to size
// two bump allocations, then `iter.reset()` and re-walked. With per-entry
// copies the pre-sizing pass is dead — emit directly.
// PERF(port): was single-buffer key arena; now per-entry Vec reuse.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each annotation explains &lt;em&gt;why&lt;/em&gt; the Rust diverges from the Zig — for the next agent that reads the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;108 Cargo crates&lt;/strong&gt; in a single workspace, prefixed &lt;code&gt;bun_core&lt;/code&gt;, &lt;code&gt;bun_sys&lt;/code&gt;, &lt;code&gt;bun_paths&lt;/code&gt;, &lt;code&gt;bun_jsc&lt;/code&gt;, &lt;code&gt;bun_runtime&lt;/code&gt;, &lt;code&gt;bun_js_parser&lt;/code&gt;, &lt;code&gt;bun_js_printer&lt;/code&gt;, &lt;code&gt;bun_bundler&lt;/code&gt;, &lt;code&gt;bun_install&lt;/code&gt;, … Tiny crates aren’t just an architectural choice — they’re a parallelism boundary. Each crate is a sharded work unit an agent can own, check, and finish independently with &lt;code&gt;cargo check -p &amp;lt;crate&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hard-coded branch naming.&lt;/strong&gt; &lt;code&gt;CLAUDE.md&lt;/code&gt; line 266: &lt;em&gt;“Branch names must start with &lt;code&gt;claude/&lt;/code&gt;. This is a requirement for the CI to work.”&lt;/em&gt; The CI distinguishes agent commits from human ones.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A prescriptive style guide masquerading as developer docs.&lt;/strong&gt; &lt;code&gt;src/CLAUDE.md&lt;/code&gt; (14 KB) contains a six-column table telling you which &lt;code&gt;std::&lt;/code&gt; APIs are forbidden and what to use instead — &lt;code&gt;bun_sys::File&lt;/code&gt; over &lt;code&gt;std::fs::File&lt;/code&gt;, &lt;code&gt;bun_paths::dirname&lt;/code&gt; over &lt;code&gt;Path::parent&lt;/code&gt;, &lt;code&gt;bun_core::env_var::*::get()&lt;/code&gt; over &lt;code&gt;std::env::var&lt;/code&gt;, and so on. No discovery, no judgment. The agents don’t get to “find a Rusty equivalent.” They get told.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A “code review self-check” section&lt;/strong&gt; in the same file:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before writing code that makes a non-obvious choice, pre-emptively ask “why this and not the alternative?” If you can’t answer, research until you can — don’t write first and justify later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And, bluntly, in the Important Notes section:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Be humble &amp;amp; honest&lt;/strong&gt; — NEVER overstate what you got done or what actually works in commits, PRs or in messages to the user.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last one only exists because someone — many someones — were overstating.&lt;/p&gt;
&lt;h2&gt;The phases as a thinking pattern&lt;/h2&gt;
&lt;p&gt;Read the phase names as a sentence, not a list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A&lt;/strong&gt; — implement a first draft (one agent per file)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;B0–B2&lt;/strong&gt; — break dependency cycles, move types between crates, tier them so each crate compiles&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt; — sweep panics out of the codebase (“panic-swarm”)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;D&lt;/strong&gt; — fan out per-crate, per-subtree, per-bundler-file work; sweep TODOs and audit &lt;code&gt;unsafe&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E&lt;/strong&gt; — port real function bodies on top of the now-compiling skeleton&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;F–G&lt;/strong&gt; — swarm probes and tests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;H&lt;/strong&gt; — diff review, dedup, idiom audit, libuv parity, &lt;code&gt;unsafe&lt;/code&gt; wrap-up, CI cleanup&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The shape is a draft, then a structural pass to make it compile, then bodies, then tests, then audits. Each phase has different invariants, different inputs, different verifier prompts. A single “port this codebase” mega-prompt would have produced slop — broken down this way, each phase becomes a small enough problem to give to an agent with a tight contract.&lt;/p&gt;
&lt;h2&gt;Portable lessons&lt;/h2&gt;
&lt;p&gt;If you take one thing from this, take this: the workflow scripts leaked because &lt;strong&gt;the agent infrastructure was project-specific, not generic.&lt;/strong&gt; Jarred didn’t run “Claude, port my repo.” He built a porting harness for his repo, with his conventions, his crate layout, and his style guide, and then ran agents through it. The model is the same one anyone can use; the leverage came from the harness around it.&lt;/p&gt;
&lt;p&gt;Specific patterns worth stealing:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Keep the old code in-tree as ground truth.&lt;/strong&gt; Don’t delete the Zig source the day you check in the Rust. Agents lose track of intent between sessions; the original is the only stable reference for “what is this &lt;em&gt;supposed&lt;/em&gt; to do.” 1,292 .zig files weren’t dead weight — they were the spec.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Annotate every divergence with WHY, not WHAT.&lt;/strong&gt; &lt;code&gt;PORT NOTE: reshaped for borrowck — drop deflate borrow before re-borrowing self&lt;/code&gt;. The next agent doesn’t need to be told that Rust has a borrow checker. It needs to be told that &lt;em&gt;this specific line&lt;/em&gt; exists because a previous agent already fought through the obvious naive version and that version didn’t work. Without this, every agent re-discovers and re-litigates the same workarounds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Write a prescriptive style guide, not an aspirational one.&lt;/strong&gt; If you have an opinion, encode it as a forbidden/replacement table. Agents are excellent rule-followers and terrible taste-havers. Don’t let them choose between &lt;code&gt;std::fs::File&lt;/code&gt; and your wrapper — say &lt;em&gt;you must use the wrapper, here is the wrapper, here is when to call which method&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Split implement from verify from apply.&lt;/strong&gt; The phase-A workflow had three roles per file: drafter, adversarial reviewer, fixer. The adversarial agent can’t see the drafter’s reasoning, only its output and the rules. This forces the drafter to be explicit (because the reviewer will catch implicit assumptions) and stops the kind of self-validating drift that single-agent loops produce.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Demand structured outputs between phases.&lt;/strong&gt; &lt;code&gt;IMPL_SCHEMA&lt;/code&gt; and &lt;code&gt;VERIFY_SCHEMA&lt;/code&gt; weren’t optional — the workflow validates them. When agents hand off to other agents, free-form prose loses information. Make them fill in a JSON object with &lt;code&gt;rs_path&lt;/code&gt;, &lt;code&gt;confidence&lt;/code&gt;, &lt;code&gt;todos&lt;/code&gt;, &lt;code&gt;rs_loc&lt;/code&gt;, &lt;code&gt;note&lt;/code&gt;. The next phase parses that, doesn’t re-read essays.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. Shard the work for parallelism, lock the writes.&lt;/strong&gt; ~200 small crates make parallelism cheap because the cargo check granularity matches the agent granularity. A flock guard around the working tree makes the parallelism &lt;em&gt;safe&lt;/em&gt;. Both are needed — the architecture and the mechanism.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. Tag agent work in CI.&lt;/strong&gt; &lt;code&gt;claude/&lt;/code&gt;-prefixed branches give you a way to gate, count, and roll back agent-produced changes separately from human ones. This is the boring kind of infrastructure that becomes essential the moment your agent volume exceeds your review volume.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8. Write rules to fight confabulation explicitly.&lt;/strong&gt; “NEVER overstate what you got done.” “Pre-emptively ask why this and not the alternative.” These read like leadership advice, but in context they’re load-bearing — they exist because the failure mode of an agent saying “all tests pass” when seven crates didn’t compile is real, and it has to be addressed in the system prompt, not at code review time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9. Make &lt;code&gt;build&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; one command.&lt;/strong&gt; &lt;code&gt;bun bd test &amp;lt;file&amp;gt;&lt;/code&gt; builds and runs. Agents don’t have to remember a two-step incantation, which means they don’t fail by forgetting step two. “Get your tests to pass. If you didn’t run the tests, your code does not work” only works as an instruction if running tests is a single tool call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10. Treat the meta-infrastructure as part of the work.&lt;/strong&gt; The leaked &lt;code&gt;.apply-lock-guard.sh&lt;/code&gt; is four lines. The leaked workflows are 4,000. The CLAUDE.md files in the repo are another 30 KB. None of this ships to users. All of it shipped Bun.&lt;/p&gt;
&lt;h2&gt;The footnote you actually want&lt;/h2&gt;
&lt;p&gt;Bun’s Rust binary is 3–8 MB smaller, the tests still pass, the benchmarks are neutral-to-faster, and the codebase is now in a language where the compiler can catch the bug class that has historically cost the team the most time. The proof of the approach is not the workflows — it’s that one of the most hostile software-engineering tasks imaginable (rewriting a million-line systems codebase in a language with a completely different memory model, without breaking compatibility) shipped, with all tests green, in a single PR.&lt;/p&gt;
&lt;p&gt;If you’re considering whether agents can do real work: they can. The question is whether you’re willing to build the harness.&lt;/p&gt;
</content></entry><entry><title>How to Run Playwright with Deno</title><id>https://www.kapp.technology/en/blog/run-playwright-on-deno-javascript-runtime</id><updated>2024-12-23T00:00:00+00:00</updated><link href="https://www.kapp.technology/en/blog/run-playwright-on-deno-javascript-runtime" rel="alternate"/><published>2024-12-23T00:00:00+00:00</published><summary>Besides Node, the alternative JavaScript runtime environment Deno is now available in 2.1 LTS. Let&apos;s look at how to get the UI test automation tool Playwright running with it.</summary><content type="html">&lt;h2&gt;JavaScript and TypeScript Runtimes&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Deno 2.1 LTS has emerged as an alternative to Node.js. We’re testing how to use the UI test automation tool Playwright with it. This article demonstrates the setup and compares the performance.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Deno is pronounced as ‘DEE-noh’. Since version &lt;a href=&quot;https://github.com/denoland/deno&quot; title=&quot;Deno Project&quot;&gt;Deno&lt;/a&gt; 2.1, the runtime environment for JavaScript, TypeScript, and WebAssembly (WASM) has been offered in a Long Term Support (LTS) variant, where critical bugs and security issues are fixed for 6 months while the APIs remain stable. These semi-annual releases are published under the MIT license.&lt;/p&gt;
&lt;p&gt;Since Deno is delivered as a single binary file, installation is very simple, even on CI/CD pipelines.&lt;/p&gt;
&lt;p&gt;To demonstrate compatibility with Node, we’ll use the UI tests for this website as an example.&lt;/p&gt;
&lt;p&gt;These UI tests were implemented using &lt;a href=&quot;https://playwright.dev/&quot; title=&quot;Playwright&quot;&gt;Playwright&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Node 22&lt;/h3&gt;
&lt;p&gt;For projects without Playwright, the tool can be added using &lt;code&gt;npm add playwright&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx playwright install
npx playwright test kapp.technology
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Deno 2.1&lt;/h3&gt;
&lt;p&gt;In Node projects, packages must be prefixed with &lt;code&gt;npm:&lt;/code&gt;. It’s necessary to explicitly grant Deno access to network, disks, and packages. The convenient blanket approach is using &lt;code&gt;--allow-all&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;deno install
deno run --allow-all npm:playwright install
deno run --allow-all npm:playwright test kapp.technology
deno run --allow-all npm:playwright show-report
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find more information about this in the GitHub issue &lt;a href=&quot;https://github.com/denoland/deno/issues/16899#issuecomment-2442248268&quot; title=&quot;NPM: Playwright does not work #16899&quot;&gt;NPM: Playwright does not work #16899&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;The Test Script&lt;/h2&gt;
&lt;p&gt;The script verifies the basic correctness of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the local website&lt;/li&gt;
&lt;li&gt;the pull or merge request and&lt;/li&gt;
&lt;li&gt;the published website&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { test, expect } from &apos;@playwright/test&apos;;

test(&apos;test&apos;, async ({ page }) =&amp;gt; {

  // Go to https://www.kapp.technology/
  await page.goto(&apos;https://www.kapp.technology/&apos;);

  // Click header &amp;gt;&amp;gt; text=Kapp Technology
  await page.locator(&apos;header &amp;gt;&amp;gt; text=Kapp Technology&apos;).click();
  await expect(page).toHaveURL(&apos;https://www.kapp.technology/&apos;);

  // Click nav &amp;gt;&amp;gt; text=Angebot
  await page.getByRole(&apos;navigation&apos;).getByRole(&apos;link&apos;, { name: &apos;Angebot&apos; }).click();
  await expect(page).toHaveURL(&apos;https://www.kapp.technology/angebot-und-services/&apos;);

  // Click text=Angebot Blog Kontakt
  await page.getByRole(&apos;link&apos;, { name: &apos;Blog&apos; }).click();
  await expect(page).toHaveURL(&apos;https://www.kapp.technology/blog/&apos;);

  // Click text=Der Neubau unserer Website
  await page.getByText(&apos;Der Neubau unserer Website&apos;).click();
  await expect(page).toHaveURL(&apos;https://www.kapp.technology/blog/relaunch-unserer-website/&apos;);

  // Click footer &amp;gt;&amp;gt; text=Angebot
  await page.getByRole(&apos;contentinfo&apos;).getByRole(&apos;link&apos;, { name: &apos;Angebot&apos; }).click();
  await expect(page).toHaveURL(&apos;https://www.kapp.technology/angebot-und-services/&apos;);

  // Click text=Jetzt Kontakt aufnehmen
  await page.getByRole(&apos;link&apos;, { name: &apos;Jetzt Kontakt aufnehmen&apos; }).click();
  await expect(page).toHaveURL(&apos;https://www.kapp.technology/contact/&apos;);

  // Click div[role=&quot;main&quot;]:has-text(&quot;Hallo. Say Hello Senden&quot;)
  await page.locator(&apos;div[role=&quot;main&quot;]:has-text(&quot;Hallo. Say Hello Senden&quot;)&apos;).click();
  await page.getByPlaceholder(&apos;Email Address&apos;).click();
  await page.getByPlaceholder(&apos;Email Address&apos;).fill(&apos;hello@example.com&apos;);
  await page.getByPlaceholder(&apos;What would you like to say?&apos;).fill(&apos;Hello from Test Automation.&apos;);
  await page.getByRole(&apos;button&apos;, { name: &apos;Senden&apos; }).click();
  await expect(page.getByRole(&apos;button&apos;, {
    name: &apos;Nachricht abgeschickt!&apos;
  })).toBeVisible();

});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;These spontaneous performance tests with just one test script can only give an impression: Both platforms are roughly equally fast.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;JavaScript Platform&lt;/th&gt;&lt;th&gt;Target&lt;/th&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;User&lt;/th&gt;&lt;th&gt;System&lt;/th&gt;&lt;th&gt;CPU&lt;/th&gt;&lt;th&gt;Total&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Deno 2.1&lt;/td&gt;&lt;td&gt;local&lt;/td&gt;&lt;td&gt;deno run –allow-all npm:playwright test local&lt;/td&gt;&lt;td&gt;18.60s&lt;/td&gt;&lt;td&gt;3.37s&lt;/td&gt;&lt;td&gt;186%&lt;/td&gt;&lt;td&gt;11.787&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Deno 2.1&lt;/td&gt;&lt;td&gt;public&lt;/td&gt;&lt;td&gt;deno run –allow-all npm:playwright test kapp.technology&lt;/td&gt;&lt;td&gt;33.92s&lt;/td&gt;&lt;td&gt;6.09s&lt;/td&gt;&lt;td&gt;201%&lt;/td&gt;&lt;td&gt;19.816s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Node 22&lt;/td&gt;&lt;td&gt;local&lt;/td&gt;&lt;td&gt;npx playwright test local&lt;/td&gt;&lt;td&gt;18.11s&lt;/td&gt;&lt;td&gt;2.91s&lt;/td&gt;&lt;td&gt;186%&lt;/td&gt;&lt;td&gt;11.269&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Node 22&lt;/td&gt;&lt;td&gt;public&lt;/td&gt;&lt;td&gt;npx playwright test kapp.technology&lt;/td&gt;&lt;td&gt;33.67s&lt;/td&gt;&lt;td&gt;5.21s&lt;/td&gt;&lt;td&gt;205%&lt;/td&gt;&lt;td&gt;18.931s&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;Benefits&lt;/h2&gt;
&lt;p&gt;As developers, we write the UI tests locally and then run them in continuous integration pipelines or against the published website.&lt;/p&gt;
&lt;p&gt;When each pull request can be easily deployed and tested like with Netlify, it brings a great gain in security. That’s when it becomes worthwhile to invest in Playwright test scripts.&lt;/p&gt;
</content></entry><entry><title>Generate resource-friendly static websites</title><id>https://www.kapp.technology/en/blog/rust-static-website-generators</id><updated>2022-08-04T00:00:00+00:00</updated><link href="https://www.kapp.technology/en/blog/rust-static-website-generators" rel="alternate"/><published>2022-08-04T00:00:00+00:00</published><summary>Thanks to Rust, three new generators create static websites from Markdown very efficiently.</summary><content type="html">&lt;p&gt;&lt;strong&gt;Thanks to Rust, three new generators create static websites from Markdown very efficiently.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are a number of legacy website generators written in Rust:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.getzola.org/&quot; title=&quot;Zola&quot;&gt;Zola&lt;/a&gt;, 2018&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cobalt-org/cobalt.rs&quot; title=&quot;Cobalt&quot;&gt;Cobalt&lt;/a&gt;, 2014&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rust-lang/mdBook&quot; title=&quot;mdbook&quot;&gt;mdBook&lt;/a&gt;, 2015&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to learn how to write a simple generator in Rust, you will find an &lt;a href=&quot;https://kerkour.com/rust-static-site-generator&quot;&gt;interesting blog post here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A challenge for HTML generators written in Rust is that they need a template engine that translates content with layouts into HTML pages &lt;em&gt;at runtime&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;The off spring🔗&lt;/h2&gt;
&lt;p&gt;The current generation of website generators implemented in Rust has learned from the ergonomics and simplicity of &lt;a href=&quot;https://nextjs.org/&quot;&gt;Nextjs&lt;/a&gt;, &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; und &lt;a href=&quot;https://www.gatsbyjs.com/&quot;&gt;Gatsby&lt;/a&gt; - all written in Javascript - it seems.&lt;/p&gt;
&lt;h3&gt;Pylon&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jayson-lennon/pylon&quot;&gt;Pylon&lt;/a&gt; is a static website generator that can be easily integrated into existing shell scripts. The following special features are implemented:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Link Checker&lt;/li&gt;
&lt;li&gt;Copy data directories into the website structure&lt;/li&gt;
&lt;li&gt;Global metadata can be made accessible to all pages&lt;/li&gt;
&lt;li&gt;Asset are located in the same directory as the content&lt;/li&gt;
&lt;li&gt;Freely definable shortcodes for use in markdown&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like Zola, Pylon uses &lt;a href=&quot;https://github.com/pallets/jinja&quot;&gt;Jinja&lt;/a&gt; templates implemented with &lt;a href=&quot;https://github.com/Keats/tera&quot;&gt;Tera&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Oinky&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/askonomm/oinky&quot;&gt;Oinky&lt;/a&gt; uses &lt;a href=&quot;https://handlebarsjs.com/&quot;&gt;Handlebars&lt;/a&gt; as a template language to translate markdown files into a static HTML website.&lt;/p&gt;
&lt;p&gt;The special feature of Oinky is the DSL, which allows the creation of data structures that can then be processed with handlebars.&lt;/p&gt;
&lt;h3&gt;Blades, 2020&lt;/h3&gt;
&lt;p&gt;[Blades][13]is fast and reduced to the essential features. With [Ramhorns][14], Blades uses a fast, [Mustache][15] - based template engine with no &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt; expressions or loops.
Blades claims to be 10x faster than the speedy [Hugo][16], but doesn’t provide any benchmarks to prove it.&lt;/p&gt;
&lt;p&gt;As with Zola, themes are also supported, but the amount of style templates is limited compared to Hugo.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to process tens to hundreds of thousands of Markdown files, you can use the generators presented here to save a lot of time and maybe even money when updating static websites at high frequency.&lt;/p&gt;
&lt;p&gt;#is_material&lt;/p&gt;
</content></entry><entry><title>The New Kid On The Block: Microsoft Playwright</title><id>https://www.kapp.technology/en/blog/web-application-testing-playwright</id><updated>2022-05-09T00:00:00+00:00</updated><link href="https://www.kapp.technology/en/blog/web-application-testing-playwright" rel="alternate"/><published>2022-05-09T00:00:00+00:00</published><summary>Testing web applications is unpopular because it is tedious. Test automation is a prerequisite for flexible development of a high-quality web application.</summary><content type="html">&lt;p&gt;&lt;strong&gt;Testing web applications is unpopular with many developers because it is tedious. Test automation is a prerequisite for efficient development of a high quality web application. Features can be delivered to customers faster, because changes can be delivered within minutes instead of days. Microsoft has launched Playwright, a new end-to-end testing tool that makes a lot of things easier.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://playwright.dev/&quot; title=&quot;Playwright&quot;&gt;Playwright&lt;/a&gt; is designed to support ‘every’ web browser on ‘any’ operating system. In fact, web applications can be tested with modern browsers such as Chrome, Firefox, Safari, Edge under the operating systems of Apple, Linux and Windows. In each case, the Chromium, WebKit and Firefox rendering engines, which are available as open source, are used. The tool also supports tests with mobile browser versions.&lt;/p&gt;
&lt;h2&gt;Polyglot🔗&lt;/h2&gt;
&lt;p&gt;Tests can be written in the most used programming languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Typescript, JavaScript&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;.NET&lt;/li&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This breadth browser and platform support is made possible by the approach of relying on the &lt;a href=&quot;https://chromedevtools.github.io/devtools-protocol/&quot;&gt;Chrome Debugger Protokoll&lt;/a&gt; protocol.&lt;/p&gt;
&lt;p&gt;Tests are processed in parallel. They wait until the browser has loaded the page. Developers have a test inspector and a test generator at hand as support.&lt;/p&gt;
&lt;p&gt;Playwright can also very easily take images of the web page and use them as a basis for test cases. Videos of the test process can also be generated.&lt;/p&gt;
&lt;p&gt;An example can illustrate how to write non-blocking tests with async and await, here in JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;	const { test, expect } = require(&apos;@playwright/test&apos;);
	
	test(&apos;basic test&apos;, async ({ page }) =&amp;gt; {  
	
	  await page.goto(&apos;https://github.com/login&apos;);
	
	  await page.fill(&apos;input[name=&quot;login&quot;]&apos;, &apos;USER NAME&apos;);
	
	  await page.fill(&apos;input[name=&quot;password&quot;]&apos;, &apos;PASSWORD&apos;);
	
	  await page.click(&apos;text=Sign in&apos;);
	
	});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Developers write the tests locally in their development environment and then run them integrated in the Continuous Integration pipelines.&lt;/p&gt;
&lt;h2&gt;Conclusion🔗&lt;/h2&gt;
&lt;p&gt;By relying on locator functions, Playwright reduces the brittleness of tests we have experienced in the past with other testing tools.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;test(&apos;display image&apos;, async ({ page }) =&amp;gt; {
  const url = &apos;about/&apos;;
  await page.goto(baseURL + url);
  await expect(page).toHaveURL(baseURL + url);
  console.log(page.url)
  expect(await page.locator(&apos;img[alt=&quot;Andreas Kapp&quot;]&apos;).screenshot()).toMatchSnapshot(&apos;about.png&apos;);

});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One of the major obstacles to test automation so far has been the cost and effort of maintaining the tests in the long run.&lt;/p&gt;
&lt;p&gt;Today seems to be the day when we see Selenium and Cypress disappearing in the rearview mirror.&lt;/p&gt;
</content></entry><entry><title>Refactoring Of Our Website</title><id>https://www.kapp.technology/en/blog/relaunch-unserer-website</id><updated>2019-11-24T00:00:00+00:00</updated><link href="https://www.kapp.technology/en/blog/relaunch-unserer-website" rel="alternate"/><published>2019-11-24T00:00:00+00:00</published><summary>On the road through the world wide web with Zola and Rust.</summary><content type="html">&lt;p&gt;After building our website with Jekyll for several years, we decided to migrate to Zola.&lt;/p&gt;
&lt;p&gt;The reason: Jekyll uses Ruby. Updates on our Mac’s were way too time-consuming. Time to move on.&lt;/p&gt;
&lt;p&gt;Our new website based on JamStack uses &lt;a href=&quot;https://www.rust-lang.org/&quot;&gt;Rust&lt;/a&gt; as backend programming language. Our CI/CD pipeline benefits from very fast builds. Alternatively, you may also rely on a static website generator called ‘&lt;a href=&quot;https://gohugo.io/&quot;&gt;Hugo&lt;/a&gt;’, which is written in Go and is quite popular as well.&lt;/p&gt;
&lt;p&gt;Click here to find more about &lt;a href=&quot;https://www.getzola.org/&quot;&gt;Zola&lt;/a&gt; and the Jinja2-based template engine &lt;a href=&quot;https://tera.netlify.com/&quot;&gt;Tera&lt;/a&gt;. Zola is very handy, however the quite good documentation lags a bit behind the releases and is quite developer-centric.&lt;/p&gt;
</content></entry><entry><title>What we are doing right now: PraxKit</title><id>https://www.kapp.technology/en/blog/was-wir-gerade-tun-praxis-administration-praxkit</id><updated>2017-07-14T00:00:00+00:00</updated><link href="https://www.kapp.technology/en/blog/was-wir-gerade-tun-praxis-administration-praxkit" rel="alternate"/><published>2017-07-14T00:00:00+00:00</published><summary>The genius simple medical practice app. Aswell for on the go.</summary><content type="html">&lt;p&gt;A the start of June we went live with &lt;a href=&quot;https://www.praxkit.ch/&quot;&gt;PraxKit&lt;/a&gt;. We developed the product in cooperation with Andrea from &lt;a href=&quot;http://kapp.ch&quot;&gt;Kapp Coaching &amp;amp; Therapie&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The App contains everything that’s needed from finding an appointment until making the billing.&lt;/p&gt;
&lt;p&gt;An attractive solution for making the billing is included. Our goals: As simple and portable as possible. Therefore the PraxKit app runs on the smartphone. But also on the computer.&lt;/p&gt;
&lt;p&gt;Currently we are insentivly working on the User Interface Design and the development of a simple procedure in the medical practice administration.&lt;/p&gt;
&lt;p&gt;Our clients and audience are independent therapists.&lt;/p&gt;
&lt;p&gt;Do you want to stay up-to-date with the development of Praxkit, then register yourself here &lt;a href=&quot;https://www.praxkit.ch/#news&quot;&gt;News&lt;/a&gt; or contact us through the website here &lt;a href=&quot;https://www.praxkit.ch/#kontakt&quot;&gt;www.praxkit.ch&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See you soon!&lt;/p&gt;
</content></entry><entry><title>Announcing Kapp Technology</title><id>https://www.kapp.technology/en/blog/announcing-kapp-technology</id><updated>2017-03-30T00:00:00+00:00</updated><link href="https://www.kapp.technology/en/blog/announcing-kapp-technology" rel="alternate"/><published>2017-03-30T00:00:00+00:00</published><summary>From Artooa emerges Kapp Technology. This is are our offerings..</summary><content type="html">&lt;p&gt;Our offerings: Research, consulting and software engineering. We use our knowledge and experience to create sustainable solutions.&lt;/p&gt;
</content></entry></feed>