<?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: Jura</title>
    <description>The latest articles on DEV Community by Jura (@__c500e8ac9bc2).</description>
    <link>https://dev.to/__c500e8ac9bc2</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%2F1728609%2F0a2482f3-5a07-49de-b6f7-d76f4624fa51.jpg</url>
      <title>DEV Community: Jura</title>
      <link>https://dev.to/__c500e8ac9bc2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__c500e8ac9bc2"/>
    <language>en</language>
    <item>
      <title>Your product has features nobody uses. Here's how to find them automatically.</title>
      <dc:creator>Jura</dc:creator>
      <pubDate>Tue, 16 Jun 2026 14:56:50 +0000</pubDate>
      <link>https://dev.to/__c500e8ac9bc2/your-product-has-features-nobody-uses-heres-how-to-find-them-automatically-3baa</link>
      <guid>https://dev.to/__c500e8ac9bc2/your-product-has-features-nobody-uses-heres-how-to-find-them-automatically-3baa</guid>
      <description>&lt;p&gt;Eight months ago I was in a product review meeting where someone asked: "does anyone actually use the bulk export?"&lt;/p&gt;

&lt;p&gt;Nobody knew. The PM checked Mixpanel. The event wasn't there. Someone said it might be tracked under a different name. We ended up skipping the question and moving on.&lt;/p&gt;

&lt;p&gt;Two weeks later, the same thing happened with a different feature. Then again.&lt;/p&gt;

&lt;p&gt;That's when I understood the real problem — and it wasn't "we need better analytics."&lt;/p&gt;




&lt;h2&gt;
  
  
  The actual problem isn't the data. It's the catalog.
&lt;/h2&gt;

&lt;p&gt;Every analytics tool I've used works the same way: you decide what to track, you add &lt;code&gt;.track()&lt;/code&gt; calls, data starts flowing. The tool shows you what events exist.&lt;/p&gt;

&lt;p&gt;But what about features that were tracked under the wrong name? What about the helper function someone added six months ago that wraps the SDK — did it ever get registered in Mixpanel? What about the feature that was renamed in code but the old event name is still in the dashboard, looking alive?&lt;/p&gt;

&lt;p&gt;You can't find dead features if you don't have a reliable catalog of what features exist.&lt;/p&gt;

&lt;p&gt;That's the gap I kept running into. And that's what I tried to fix with &lt;a href="https://eventra.dev" rel="noopener noreferrer"&gt;Eventra&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The insight: build the catalog from the code
&lt;/h2&gt;

&lt;p&gt;Instead of asking teams to manually define what they track, Eventra reads it from the TypeScript codebase itself.&lt;/p&gt;

&lt;p&gt;The CLI uses the TypeScript compiler API to scan your project and find every &lt;code&gt;eventra.track()&lt;/code&gt; call — including through wrapper functions, re-exports, and barrel files.&lt;/p&gt;

&lt;p&gt;Let me show you what that means in practice.&lt;/p&gt;

&lt;p&gt;Say you have this in your codebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/analytics.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;Eventra&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;@eventra_dev/eventra-sdk&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;sdk&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;Eventra&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&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;EVENTRA_KEY&lt;/span&gt;&lt;span class="o"&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;function&lt;/span&gt; &lt;span class="nf"&gt;trackFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&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;// features/export.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;trackFeature&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;@/lib/analytics&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;function&lt;/span&gt; &lt;span class="nf"&gt;handleExport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;trackFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bulk_export&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;Most analytics tools would never know &lt;code&gt;trackFeature&lt;/code&gt; wraps the SDK. You'd have to manually register it. Eventra finds the chain automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @eventra_dev/eventra-cli &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;span class="c"&gt;# Scanning...&lt;/span&gt;
&lt;span class="c"&gt;# Found 14 events, 1 function wrapper (trackFeature → eventra.track)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does this by building a semantic model of your code — resolving imports, following the call graph, extracting what string each &lt;code&gt;name&lt;/code&gt; argument resolves to. Not a regex pass. The actual TypeScript compiler API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dead features fall out naturally
&lt;/h2&gt;

&lt;p&gt;Once you have a complete, code-derived catalog, dead feature detection is trivial: which catalog entries have had no events in the last N days?&lt;/p&gt;

&lt;p&gt;The dashboard surfaces these in a dedicated view. No configuration. No manually defining what "dead" means. The data tells you.&lt;/p&gt;

&lt;p&gt;What's more useful is the three-state classification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Active&lt;/strong&gt; — at least one event in the last 14 days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dead&lt;/strong&gt; — had events before, nothing recently. This is your deletion list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never seen&lt;/strong&gt; — in your code, never reached the platform. Misconfigured, unreachable, or behind a flag that never fires.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That third state was the one that surprised me most when I built this. Features that exist in code but have never, in the history of the product, produced a single event. Either they're behind a toggle that was never turned on, or the tracking is broken, or the feature is so buried nobody found it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keeping the catalog in sync: CI integration
&lt;/h2&gt;

&lt;p&gt;The catalog drifts. You add a feature, forget to run &lt;code&gt;sync&lt;/code&gt;, and now the dead feature list is incomplete. Or you remove a feature but the old name stays in the catalog, giving false confidence that something is being tracked when the code is gone.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;check&lt;/code&gt; command is the CI gate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @eventra_dev/eventra-cli check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It compares the current state of the codebase against &lt;code&gt;eventra.json&lt;/code&gt; (the committed catalog snapshot) and exits 1 if they differ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Checking events and function wrappers...

Events:
+ new_feature_name   ← in code, not in catalog
- old_feature_name   ← in catalog, removed from code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to your CI pipeline and the catalog never drifts without someone noticing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The SDK side
&lt;/h2&gt;

&lt;p&gt;The CLI handles catalog management. The SDK handles runtime events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Eventra&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;@eventra_dev/eventra-sdk&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;eventra&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;Eventra&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;eventra&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bulk_export&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="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth mentioning about the SDK implementation that I'm happy with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero dependencies.&lt;/strong&gt; The whole thing — batching, retry with exponential backoff, circuit breaker, browser localStorage persistence, multi-tab leader election — is implemented with no runtime dependencies. Works in browser, Node.js, edge runtimes, and serverless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Circuit breaker.&lt;/strong&gt; After 5 consecutive delivery failures, the SDK stops sending for 5 seconds. This prevents hammering a struggling API endpoint and burning through retry budgets. When the cooldown expires, one request goes through in half-open mode. If it succeeds, the circuit closes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser multi-tab coordination.&lt;/strong&gt; If you have multiple tabs open, only one tab (the "leader") flushes events to the server. The queue is shared via &lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;BroadcastChannel&lt;/code&gt;. This prevents duplicate events from tab-heavy users and reduces server load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Graceful shutdown.&lt;/strong&gt; In Node.js, the SDK registers &lt;code&gt;SIGINT&lt;/code&gt;/&lt;code&gt;SIGTERM&lt;/code&gt; handlers and flushes the queue before the process exits. In the browser, it listens to &lt;code&gt;visibilitychange&lt;/code&gt; and &lt;code&gt;pagehide&lt;/code&gt;. Events sent just before the user closes the tab actually arrive.&lt;/p&gt;




&lt;h2&gt;
  
  
  The ingest pipeline
&lt;/h2&gt;

&lt;p&gt;On the server side, the bottleneck I was most worried about was write throughput. Feature events can arrive in bursts — a deploy goes out, users start using a new feature, thousands of events arrive in seconds.&lt;/p&gt;

&lt;p&gt;The solution is PostgreSQL's &lt;code&gt;COPY FROM STDIN&lt;/code&gt; with &lt;code&gt;pg-copy-streams&lt;/code&gt;. Instead of individual &lt;code&gt;INSERT&lt;/code&gt; statements, events are written to an in-memory buffer, drained in batches of up to 5,000 rows, streamed into a temp table, and atomically upserted with deduplication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"FeatureEvent"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"projectId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;tmp_events&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;registry_insert&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"projectId"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"projectId"&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"idempotencyKey"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"idempotencyKey"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;IdempotencyRegistry&lt;/code&gt; table handles deduplication: if the SDK sends the same event twice (retry after a timeout), the second insert is a no-op. Clients use UUID v4 as the idempotency key, generated client-side.&lt;/p&gt;

&lt;p&gt;If the database goes down, batches spill to disk (atomic tmp-rename write) and are recovered on restart. The API returns &lt;code&gt;{ success: true }&lt;/code&gt; as soon as events hit the in-memory buffer, so ingest latency stays low regardless of what the database is doing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned building this after work for 6 months
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The CLI was the hardest part.&lt;/strong&gt; The TypeScript compiler API is powerful but the documentation is thin. The wrapper propagation system — tracking which function parameter maps to the event name, across files, through re-exports — took multiple rewrites to get right. The breakthrough was treating &lt;code&gt;ts.Symbol&lt;/code&gt; as the canonical identity and using WeakMaps keyed on symbols for all caches. Once I had that foundation, incremental watch mode fell into place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dead features was always the core idea.&lt;/strong&gt; Everything else — the SDK, the dashboard, the billing, the workspaces — exists to support the dead feature detection. The catalog-from-code approach only works if there's a reliable way to discover what's in the code. The CLI is that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ship less&lt;/strong&gt; is underrated as a product strategy. Every feature you remove is maintenance you never have to do, bugs that can't exist, and cognitive load you save your users. Eventra is a tool for shipping less. I find that more interesting than another tool for shipping more.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you have a TypeScript project and want to see what's dead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install the SDK&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @eventra_dev/eventra-sdk

&lt;span class="c"&gt;# Initialize the CLI config&lt;/span&gt;
npx @eventra_dev/eventra-cli init

&lt;span class="c"&gt;# Scan and sync&lt;/span&gt;
npx @eventra_dev/eventra-cli &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free tier is 100k events/month, no credit card.&lt;/p&gt;

&lt;p&gt;→ &lt;a href="https://eventra.dev" rel="noopener noreferrer"&gt;eventra.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The SDK and CLI are MIT-licensed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/and-1991/eventra-sdk" rel="noopener noreferrer"&gt;github.com/and-1991/eventra-sdk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/and-1991/eventra-cli" rel="noopener noreferrer"&gt;github.com/and-1991/eventra-cli&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples:&lt;br&gt;
-&lt;a href="https://github.com/and-1991/eventra-examples" rel="noopener noreferrer"&gt;github.com/and-1991/eventra-examples&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Happy to answer questions in the comments — especially about the static analysis approach or the ingest pipeline design.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>analytics</category>
    </item>
    <item>
      <title>I Built Eventra in 6 Months (After My Day Job). Here's How.</title>
      <dc:creator>Jura</dc:creator>
      <pubDate>Fri, 10 Apr 2026 19:17:25 +0000</pubDate>
      <link>https://dev.to/__c500e8ac9bc2/i-built-eventra-in-6-months-after-my-day-job-heres-how-2j9</link>
      <guid>https://dev.to/__c500e8ac9bc2/i-built-eventra-in-6-months-after-my-day-job-heres-how-2j9</guid>
      <description>&lt;h1&gt;
  
  
  Building Eventra After Work: A 6-Month Solo Developer Journey
&lt;/h1&gt;

&lt;p&gt;For the past six months, I've been building &lt;strong&gt;Eventra&lt;/strong&gt; after work.&lt;/p&gt;

&lt;p&gt;I have a full-time job that pays the bills.&lt;br&gt;
Eventra is something I build in the evenings and on weekends.&lt;/p&gt;

&lt;p&gt;I didn’t start with a startup idea.&lt;br&gt;
I didn’t start with validation.&lt;br&gt;
I just wanted to build something properly.&lt;/p&gt;

&lt;p&gt;Not a weekend project.&lt;br&gt;
Not a demo.&lt;br&gt;
Something real.&lt;/p&gt;

&lt;p&gt;That eventually became &lt;strong&gt;Eventra&lt;/strong&gt; — a feature analytics platform focused on understanding which features users actually use.&lt;/p&gt;


&lt;h1&gt;
  
  
  Why I Started Building Eventra
&lt;/h1&gt;

&lt;p&gt;When you build products long enough, you accumulate features.&lt;/p&gt;

&lt;p&gt;Some are used every day.&lt;br&gt;
Some occasionally.&lt;br&gt;
Some… never.&lt;/p&gt;

&lt;p&gt;Over time, you lose visibility.&lt;/p&gt;

&lt;p&gt;You don't know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which features matter&lt;/li&gt;
&lt;li&gt;Which ones nobody uses&lt;/li&gt;
&lt;li&gt;What can be removed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Analytics helps — but only partially.&lt;/p&gt;

&lt;p&gt;Analytics shows what happened.&lt;br&gt;
But it doesn't show what &lt;strong&gt;exists but never happens&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That idea stuck with me.&lt;/p&gt;


&lt;h1&gt;
  
  
  Building After Work
&lt;/h1&gt;

&lt;p&gt;Building Eventra while working full-time wasn't easy.&lt;/p&gt;

&lt;p&gt;Some days I wrote code for 8 hours at work, then another 2–3 hours at night.&lt;/p&gt;

&lt;p&gt;Some weekends were spent debugging rollups instead of going outside.&lt;/p&gt;

&lt;p&gt;Progress wasn't fast — but it was steady.&lt;/p&gt;

&lt;p&gt;Over six months, Eventra slowly took shape.&lt;/p&gt;


&lt;h1&gt;
  
  
  Step 1 — Building the Core Platform
&lt;/h1&gt;

&lt;p&gt;I started with the core platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Workspaces&lt;/li&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;li&gt;Feature adoption&lt;/li&gt;
&lt;li&gt;Dead feature detection&lt;/li&gt;
&lt;li&gt;Alerts&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;Operations dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was to build something complete — not just analytics, but a system focused on feature usage.&lt;/p&gt;


&lt;h1&gt;
  
  
  What the Dashboard Shows
&lt;/h1&gt;

&lt;p&gt;The main dashboard gives a quick overview of product usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total events&lt;/li&gt;
&lt;li&gt;Unique users&lt;/li&gt;
&lt;li&gt;Active features&lt;/li&gt;
&lt;li&gt;Inactive features&lt;/li&gt;
&lt;li&gt;Usage over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps quickly understand whether your product is actually being used and how feature adoption changes over time.&lt;/p&gt;


&lt;h1&gt;
  
  
  Feature Tracking
&lt;/h1&gt;

&lt;p&gt;Eventra tracks individual features and shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total usage&lt;/li&gt;
&lt;li&gt;Unique users&lt;/li&gt;
&lt;li&gt;First used&lt;/li&gt;
&lt;li&gt;Last used&lt;/li&gt;
&lt;li&gt;Usage timeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can open any feature and see its detailed analytics and adoption patterns.&lt;/p&gt;

&lt;p&gt;This makes it easier to understand which parts of your product matter.&lt;/p&gt;


&lt;h1&gt;
  
  
  Dead Feature Detection
&lt;/h1&gt;

&lt;p&gt;One of the core ideas behind Eventra is detecting unused functionality.&lt;/p&gt;

&lt;p&gt;Eventra automatically detects features that haven't been used recently and marks them as inactive.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature not used in 14 days&lt;/li&gt;
&lt;li&gt;Feature used once but never again&lt;/li&gt;
&lt;li&gt;Feature abandoned after release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps identify functionality that might be safe to remove.&lt;/p&gt;


&lt;h1&gt;
  
  
  Events Explorer
&lt;/h1&gt;

&lt;p&gt;You can also inspect raw events.&lt;/p&gt;

&lt;p&gt;This helps when debugging or understanding how users interact with your application.&lt;/p&gt;

&lt;p&gt;You can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event name&lt;/li&gt;
&lt;li&gt;User&lt;/li&gt;
&lt;li&gt;Timestamp&lt;/li&gt;
&lt;li&gt;Usage patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it easier to understand what users actually do.&lt;/p&gt;


&lt;h1&gt;
  
  
  Workspaces and Projects
&lt;/h1&gt;

&lt;p&gt;Eventra supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple workspaces&lt;/li&gt;
&lt;li&gt;Multiple projects&lt;/li&gt;
&lt;li&gt;Team members&lt;/li&gt;
&lt;li&gt;Permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows you to organize analytics across different products.&lt;/p&gt;


&lt;h1&gt;
  
  
  API Keys and SDK Integration
&lt;/h1&gt;

&lt;p&gt;Each project has API keys for event ingestion.&lt;/p&gt;

&lt;p&gt;You can rotate keys, create multiple keys, and track usage.&lt;/p&gt;

&lt;p&gt;This allows safe integration with SDKs and backend systems.&lt;/p&gt;


&lt;h1&gt;
  
  
  Operations Dashboard
&lt;/h1&gt;

&lt;p&gt;I also built an internal operations dashboard.&lt;/p&gt;

&lt;p&gt;It shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingest throughput&lt;/li&gt;
&lt;li&gt;Buffer size&lt;/li&gt;
&lt;li&gt;Rollup lag&lt;/li&gt;
&lt;li&gt;Processing delay&lt;/li&gt;
&lt;li&gt;System health&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps monitor the analytics pipeline.&lt;/p&gt;


&lt;h1&gt;
  
  
  Interactive Demo
&lt;/h1&gt;

&lt;p&gt;There's an &lt;strong&gt;interactive demo&lt;/strong&gt; on the homepage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://eventra.dev" rel="noopener noreferrer"&gt;https://eventra.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can explore the UI, navigate between pages, and see how everything works.&lt;/p&gt;

&lt;p&gt;Some data is intentionally masked to avoid exposing real information.&lt;/p&gt;

&lt;p&gt;This makes it possible to explore the product without signing up.&lt;/p&gt;


&lt;h1&gt;
  
  
  Step 2 — The SDK
&lt;/h1&gt;

&lt;p&gt;Then I built the SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Eventra&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;@eventra_dev/eventra-sdk&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;tracker&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;Eventra&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_PROJECT_API_KEY&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;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feature_used&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="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user_123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dashboard&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;p&gt;The SDK supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch sending&lt;/li&gt;
&lt;li&gt;Retry logic&lt;/li&gt;
&lt;li&gt;Browser / Node / Edge runtimes&lt;/li&gt;
&lt;li&gt;TypeScript-first API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This worked well — but something was missing.&lt;/p&gt;

&lt;p&gt;The SDK only tracks events that actually happen.&lt;/p&gt;

&lt;p&gt;Which meant I still couldn't detect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Features that exist but aren't used&lt;/li&gt;
&lt;li&gt;Dead UI&lt;/li&gt;
&lt;li&gt;Forgotten code&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Step 3 — The CLI
&lt;/h1&gt;

&lt;p&gt;That's when I built the CLI.&lt;/p&gt;

&lt;p&gt;The CLI scans your codebase and finds all tracked events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @eventra_dev/eventra-cli init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scans your project&lt;/li&gt;
&lt;li&gt;Finds track() calls&lt;/li&gt;
&lt;li&gt;Detects wrapper components&lt;/li&gt;
&lt;li&gt;Saves configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now Eventra can compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Events in code&lt;/li&gt;
&lt;li&gt;Events actually triggered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And detect unused features.&lt;/p&gt;

&lt;p&gt;That's when Eventra became a complete product.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Hardest Parts
&lt;/h1&gt;

&lt;p&gt;Each part had its own challenges:&lt;/p&gt;

&lt;p&gt;Backend — rollup engine&lt;br&gt;
Frontend — workspace/project architecture&lt;br&gt;
CLI — AST parsing and wrapper detection&lt;/p&gt;

&lt;p&gt;The CLI ended up being one of the hardest parts technically.&lt;/p&gt;




&lt;h1&gt;
  
  
  Current Status
&lt;/h1&gt;

&lt;p&gt;After 6 months:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard — production-ready&lt;/li&gt;
&lt;li&gt;SDK — stable&lt;/li&gt;
&lt;li&gt;CLI — early version (0.0.4)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm still building this solo, after work.&lt;/p&gt;




&lt;h1&gt;
  
  
  Pricing
&lt;/h1&gt;

&lt;p&gt;I'm not trying to maximize revenue right now.&lt;/p&gt;

&lt;p&gt;The goal is validation and feedback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://eventra.dev/pricing" rel="noopener noreferrer"&gt;https://eventra.dev/pricing&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Try It
&lt;/h1&gt;

&lt;p&gt;Website&lt;br&gt;
&lt;a href="https://eventra.dev" rel="noopener noreferrer"&gt;https://eventra.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docs&lt;br&gt;
&lt;a href="https://eventra.dev/docs" rel="noopener noreferrer"&gt;https://eventra.dev/docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SDK&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/@eventra_dev/eventra-sdk" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@eventra_dev/eventra-sdk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CLI&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/@eventra_dev/eventra-cli" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@eventra_dev/eventra-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub&lt;br&gt;
&lt;a href="https://github.com/and-1991/eventra-sdk" rel="noopener noreferrer"&gt;https://github.com/and-1991/eventra-sdk&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/and-1991/eventra-cli" rel="noopener noreferrer"&gt;https://github.com/and-1991/eventra-cli&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Why I'm Sharing This
&lt;/h1&gt;

&lt;p&gt;I'm not trying to sell anything.&lt;/p&gt;

&lt;p&gt;I'm just building something useful after work.&lt;/p&gt;

&lt;p&gt;If you're also building something on the side — I'd love to hear about it.&lt;/p&gt;

&lt;p&gt;And if Eventra looks useful, I'd love your feedback.&lt;/p&gt;

</description>
      <category>node</category>
      <category>react</category>
      <category>typescript</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
