<?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: Randolf J.</title>
    <description>The latest articles on DEV Community by Randolf J. (@jrandolf).</description>
    <link>https://dev.to/jrandolf</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3797455%2Fac131e8f-e37e-4876-8704-9597e2920958.jpeg</url>
      <title>DEV Community: Randolf J.</title>
      <link>https://dev.to/jrandolf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jrandolf"/>
    <language>en</language>
    <item>
      <title>Generators are All You Need</title>
      <dc:creator>Randolf J.</dc:creator>
      <pubDate>Wed, 16 Sep 2026 00:52:22 +0000</pubDate>
      <link>https://dev.to/jrandolf/generators-are-all-you-need-a60</link>
      <guid>https://dev.to/jrandolf/generators-are-all-you-need-a60</guid>
      <description>&lt;p&gt;If you've been in development long enough, you know that one of the hardest parts of coding is building on top of existing code. I remember reading a statistic suggesting that starting a codebase from scratch is faster than building on an existing one. With AI, some of that has gotten better. But we also spend a lot of time forcing AI to work within the code we already have, and that comes with its own problems. If I were to make up a statistic myself, I'd probably say it's still faster to have an AI build something from scratch than to have it wrestle with an existing implementation.&lt;/p&gt;

&lt;p&gt;One place I keep running into this is with open source.&lt;/p&gt;

&lt;p&gt;I love open source. I've personally maintained one of the largest open source codebases on GitHub. But when you're using it inside a company, you can spend a surprising amount of time dealing with bugs, gaps in functionality, and dependencies that nobody has had time to maintain. And I get it. People are often doing this work for free. I don't think it's reasonable to expect them to deliver everything our company needs.&lt;/p&gt;

&lt;p&gt;Still, we spend a lot of time trying to make those dependencies fit our codebase.&lt;/p&gt;

&lt;p&gt;Some of the most important tools we use are generators: things that generate OpenAPI documents, internal APIs, or the scaffolding we build the rest of the application on. At one point, I spent maybe a month trying to get code generators to work. I had an AI go through OpenAPI generators across &lt;strong&gt;every&lt;/strong&gt; language and every one we tried was missing something that prevented it from handling the full specification we needed.&lt;/p&gt;

&lt;p&gt;Usually, you'd patch the dependency or vendor it and maintain your own copy. But I wanted to experiment, so I thought, why not have a coding agent build the generator?&lt;/p&gt;

&lt;p&gt;My hypothesis was pretty simple. Coding agents are good at problems where they can verify the result, and generators seem like a good fit for that. You have a specification. You have an expected output. You can define the behavior through a contract and check it with end-to-end tests. The agent has something concrete to work toward and a way to tell whether it's getting there.&lt;/p&gt;

&lt;p&gt;It took me one hour to build the generator.&lt;/p&gt;

&lt;p&gt;I remember thinking, why did I spend a month trying to force all of this to work?&lt;/p&gt;

&lt;p&gt;That experience changed how I thought about where to use AI. As these models get smarter, and I mean really smart, to the point where maybe they can even solve Navier-Stokes, I expect them to get very good at building things like compilers and generators. Even with what we have today, having an agent build a generator can be more efficient than writing and maintaining all the code it produces.&lt;/p&gt;

&lt;p&gt;So we went on a bit of an escapade and converted a vast majority of our code to generated code. By that, I mean we had an AI build the generators, then used those generators to produce code from specifications. Kind of meta, but it works.&lt;/p&gt;

&lt;p&gt;It's been much easier to maintain. Our generators contain less code than the code they produce, so we have less to work through when something needs to change. We change the generator, regenerate the code, and that change carries through the rest of the codebase. We've found that having an AI repair the generator is often more efficient than having it work through all the handwritten code we'd otherwise need to maintain.&lt;/p&gt;

&lt;p&gt;This feels like a different way of working from asking an AI to vibe code an entire app, or having a human write the whole thing by hand. We're putting more of the work into the specifications and the generators, and we're using tests to check that the generated code does what we expect.&lt;/p&gt;

&lt;p&gt;Based on my experience, I think a lot of coding could end up looking like this. We'll maintain a collection of generators and use them to produce the code we need. There's still work in defining the contract and making sure our tests cover it. But once we have a contract we can verify, we give the agent a problem it can keep testing and refining.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>opensource</category>
      <category>tools</category>
    </item>
    <item>
      <title>Readable stack traces in production: source maps + OpenTelemetry</title>
      <dc:creator>Randolf J.</dc:creator>
      <pubDate>Tue, 10 Mar 2026 10:35:11 +0000</pubDate>
      <link>https://dev.to/jrandolf/readable-stack-traces-in-production-source-maps-opentelemetry-1de3</link>
      <guid>https://dev.to/jrandolf/readable-stack-traces-in-production-source-maps-opentelemetry-1de3</guid>
      <description>&lt;p&gt;Two years ago I left Sentry and moved to OpenTelemetry. The one thing I had to rebuild was source map resolution. I built &lt;a href="https://github.com/jrandolf/smapped-traces" rel="noopener noreferrer"&gt;&lt;code&gt;smapped-traces&lt;/code&gt;&lt;/a&gt; internally to do it, and we are open sourcing it now that it has run in production for two years.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem with naive approaches
&lt;/h2&gt;

&lt;p&gt;Serving source maps from your build output exposes your source code publicly. Restricting access requires your error handler to authenticate against a source map server at resolution time, which is complexity you do not want in that path. Bundling source maps into your collector configuration does not work either; the collector has no stable way to associate a map with a minified file across deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debug IDs
&lt;/h2&gt;

&lt;p&gt;The approach I landed on uses debug IDs, a mechanism now supported by Turbopack natively and by webpack via the &lt;a href="https://github.com/tc39/ecma426/blob/main/proposals/debug-id.md" rel="noopener noreferrer"&gt;TC39 proposal&lt;/a&gt;. At build time, the bundler generates a UUID per source file and writes it into both the compiled output and the corresponding &lt;code&gt;.js.map&lt;/code&gt; as a &lt;code&gt;debugId&lt;/code&gt; field. It also injects a runtime global (&lt;code&gt;_debugIds&lt;/code&gt; for Turbopack, &lt;code&gt;__DEBUG_IDS__&lt;/code&gt; for webpack) mapping source URLs to those UUIDs.&lt;/p&gt;

&lt;p&gt;Any stack frame URL resolves to its source map without scanning or path matching.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser                     Your server             OTel collector
  │                              │                       │
  │  SourceMappedSpanExporter    │                       │
  │  (attaches debug IDs to      │                       │
  │   exception events)          │                       │
  │──────────────────────────────▶  createTracesHandler  │
  │                              │  (resolves stack      │
  │                              │   traces via store)   │
  │                              │──────────────────────▶│
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@smapped-traces/nextjs&lt;/code&gt; hooks into &lt;code&gt;runAfterProductionCompile&lt;/code&gt;, extracts the &lt;code&gt;debugId&lt;/code&gt; from every &lt;code&gt;.js.map&lt;/code&gt;, uploads the content to a store, and deletes the map files from the build output.&lt;/p&gt;

&lt;p&gt;On the client, &lt;code&gt;SourceMappedSpanExporter&lt;/code&gt; wraps your span exporter. On exception events, it reads the debug ID globals, resolves each frame URL to a debug ID, and attaches them as span attributes before forwarding as standard OTLP.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;createTracesHandler()&lt;/code&gt; runs on your server. It receives OTLP traces, resolves each debug ID against the store, rewrites the stack frames with original file and line information, and forwards to your collector. The minified trace is preserved as &lt;code&gt;exception.stacktrace.original&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup (Next.js)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;smapped-traces @smapped-traces/nextjs @smapped-traces/sqlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.mjs&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;withSourceMaps&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="s2"&gt;@smapped-traces/nextjs&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;createSqliteStore&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="s2"&gt;@smapped-traces/sqlite&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;join&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="s2"&gt;node:path&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;withSourceMaps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* your existing config */&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createSqliteStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;distDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sourcemaps.db&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// instrumentation-client.ts&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;SourceMappedSpanExporter&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="s2"&gt;smapped-traces/client&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;SimpleSpanProcessor&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="s2"&gt;@opentelemetry/sdk-trace-base&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;WebTracerProvider&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="s2"&gt;@opentelemetry/sdk-trace-web&lt;/span&gt;&lt;span class="dl"&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;exporter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SourceMappedSpanExporter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/sourcemaps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebTracerProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;spanProcessors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleSpanProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/api/sourcemaps/route.ts&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;OTLPTraceExporter&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="s2"&gt;@opentelemetry/exporter-trace-otlp-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;createTracesHandler&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="s2"&gt;smapped-traces/route&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;createSqliteStore&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="s2"&gt;@smapped-traces/sqlite&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;join&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="s2"&gt;node:path&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;POST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTracesHandler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;exporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OTLPTraceExporter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4318&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/v1/traces`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createSqliteStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.next/sourcemaps.db&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Storage
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;SourceMapStore&lt;/code&gt; is an interface over &lt;code&gt;get(debugId)&lt;/code&gt; and &lt;code&gt;put(debugId, content)&lt;/code&gt;. Three implementations are included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;createSqliteStore(path)&lt;/code&gt; — local SQLite via &lt;code&gt;better-sqlite3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createS3Store({ client, bucket, prefix? })&lt;/code&gt; — any S3-compatible backend (AWS, Cloudflare R2, GCS)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createHttpStore(url)&lt;/code&gt; — HTTP client against a &lt;code&gt;createStoreHandler()&lt;/code&gt; deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For distributed or serverless environments, point both the build plugin and the handler at the same shared store.&lt;/p&gt;




&lt;h2&gt;
  
  
  Runtime compatibility
&lt;/h2&gt;

&lt;p&gt;The handler uses standard Web &lt;code&gt;Request&lt;/code&gt;/&lt;code&gt;Response&lt;/code&gt; and has no Node.js dependencies, so it runs in Bun, Deno, Cloudflare Workers, or any edge runtime. For non-Next.js builds, source map collection is a loop over build artifacts calling &lt;code&gt;store.put(debugId, content)&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;Requires OTel SDK v2+ and Next.js 16+ for &lt;code&gt;@smapped-traces/nextjs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/jrandolf/smapped-traces" rel="noopener noreferrer"&gt;https://github.com/jrandolf/smapped-traces&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turbopack and webpack are supported. Vite and esbuild are not; support depends on whether those bundlers implement the ECMA-426 debug ID spec.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>monitoring</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
