<?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: Puneet Khandelwal</title>
    <description>The latest articles on DEV Community by Puneet Khandelwal (@puneet_khandelwal_429a72e).</description>
    <link>https://dev.to/puneet_khandelwal_429a72e</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%2F3886902%2F3e24c2b3-9760-4020-a068-aa6c1890278d.png</url>
      <title>DEV Community: Puneet Khandelwal</title>
      <link>https://dev.to/puneet_khandelwal_429a72e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/puneet_khandelwal_429a72e"/>
    <language>en</language>
    <item>
      <title>Building a Zero-Knowledge Ballot Checker for Local Elections</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Tue, 13 Oct 2026 18:35:34 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/building-a-zero-knowledge-ballot-checker-for-local-elections-hee</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/building-a-zero-knowledge-ballot-checker-for-local-elections-hee</guid>
      <description>&lt;p&gt;Trust in elections breaks the second you can't check your own vote. Last month, auditing an open-source voting tool, my engineering team hit a wall. We needed voters to prove their ballots counted without letting anyone—not even root admins—link a vote to a person. Better database encryption didn't fix it. Zero-knowledge cryptography did.&lt;/p&gt;

&lt;p&gt;Most civic tech uses centralized ledgers or simple hashes. A hash proves a record existed, but brute-force attacks expose the underlying data. Zero-knowledge proofs let a prover convince a verifier a statement is true without leaking anything else. For our voting app, the statement is straightforward. My ballot sits in the final tally and my token is valid, but my identity and candidate choice stay hidden.&lt;/p&gt;

&lt;p&gt;We tested this with an arithmetic circuit in a mock verification loop. The workflow splits into two parts. First, the voter's local device generates a witness with secret inputs like their private key and candidate ID. The client runs a proving algorithm, spitting out a cryptographic proof file that acts as a math-backed guarantee. Second, the public bulletin board verifies that proof. If the math checks out, the system counts the vote without ever looking at the ballot itself.&lt;/p&gt;

&lt;p&gt;Here is what the verification logic looks like in Node.js using a standard verification key. We pass the public inputs, like the election root hash and nullifiers, alongside the proof payload.&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verify&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;snarkjs&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;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;verifyBallotProof&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;vKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verification_key.json&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;proof&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ballot_proof.json&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;publicSignals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public_signals.json&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;isValid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;vKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publicSignals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;proof&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ballot verified successfully without exposing voter identity.&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Verification failed. Invalid proof or tampered signal.&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="nf"&gt;verifyBallotProof&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The math looks great on paper, but municipal deployments are brutal. Circuit compilation takes forever, low-end mobile browsers run out of memory generating proofs, and a botched trusted setup ceremony wrecks the whole security model. If we want cryptography to replace blind trust in election hardware, these circuits need to run smoothly on cheap phones.&lt;/p&gt;

&lt;p&gt;Code alone won't fix institutional distrust. Still, verifiable systems give communities the tools to demand accountability. When citizens can mathematically prove their election works, transparency stops being a campaign slogan and becomes an engineering standard.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>civic</category>
      <category>policy</category>
    </item>
    <item>
      <title>The Feature Flag Technical Debt Trap Nobody Talks About</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Mon, 12 Oct 2026 17:21:00 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/the-feature-flag-technical-debt-trap-nobody-talks-about-424e</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/the-feature-flag-technical-debt-trap-nobody-talks-about-424e</guid>
      <description>&lt;p&gt;We traded branch hell for boolean hell, and somehow convinced ourselves it was progress.&lt;/p&gt;

&lt;p&gt;Feature flags promised continuous delivery utopia. Push to main whenever you want, wrap the unfinished work in an &lt;code&gt;if (isFeatureEnabled('cool-new-ui'))&lt;/code&gt; block, and ship it safely. On paper, it sounds like the ultimate developer tool. In practice, codebases everywhere are rotting from the inside out with abandoned conditionals that nobody dares to touch.&lt;/p&gt;

&lt;p&gt;Here is the reality eighteen months after adopting aggressive flagging. You stop writing modular code and start writing branched monoliths. A single route handler ends up looking like a nested maze of legacy checks. You have flags checking for flags. You have flags checking for user tiers, region rollouts, beta cohorts, and ancient experiments from two product managers who left the company last year. The compiler cannot help you here because these are runtime strings. Static analysis tools choke on the combinatorial explosion of states.&lt;/p&gt;

&lt;p&gt;Look at a standard piece of modern backend drift. It usually starts clean:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v2-dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;renderNewDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;renderLegacyDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;Fast forward two years. The feature won, but nobody removed the flag. Now it looks like this:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&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;isV2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v2-dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&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;isDark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark-mode-beta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isV2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enterprise&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;renderEnterpriseV2Dark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;renderNewDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isDark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;legacy-patch-99&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;renderPatchedLegacy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;renderLegacyDashboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;With traditional branch-based deployments, dead code gets pruned naturally during code review or caught by git history when a feature branch goes stale. When a pull request sits around too long, you close it. The dead code vanishes with it. But with feature flags, dead code lives proudly in your main branch, executing checks against a remote service on every single request, introducing network latency and invisible logic paths.&lt;/p&gt;

&lt;p&gt;The industry solved continuous integration by building robust tooling for merging code. We need to treat feature flag hygiene with the exact same rigor. If a flag doesn't have an automated removal ticket scheduled for the day after full rollout, don't merge it. Better yet, we need language-level semantics that treat flags as ephemeral compilation targets rather than permanent runtime configurations.&lt;/p&gt;

&lt;p&gt;Until we treat a boolean toggle with the same disdain as a messy merge conflict, our codebases will continue to drown in conditional debt.&lt;/p&gt;

</description>
      <category>developertools</category>
      <category>coding</category>
      <category>saas</category>
    </item>
    <item>
      <title>Why Your Productivity System Keeps Crashing</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Sun, 11 Oct 2026 16:19:02 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/why-your-productivity-system-keeps-crashing-3d28</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/why-your-productivity-system-keeps-crashing-3d28</guid>
      <description>&lt;p&gt;If you spend all day debugging async race conditions, why do you design your personal routine like a monolithic shell script that fails silently at the first error? We've all done it. You spend a Sunday evening building the ultimate task management setup, color-coding tags, and nesting projects seven levels deep. By Tuesday afternoon, an unexpected meeting throws off the execution stack, and the whole system collapses into a guilt-ridden mess.&lt;/p&gt;

&lt;p&gt;The shift happening right now in personal productivity points away from rigid optimization and toward fault-tolerant living. Instead of aiming for 100 percent uptime, people build routines that handle exceptions gracefully. When you code for production, you assume the network will drop and memory leaks will happen. Yet when we plan our weeks, we assume zero friction, endless energy, and absolute mental clarity.&lt;/p&gt;

&lt;p&gt;This matters because hyper-optimized systems create brittle habits. When every hour is tightly coupled to a specific output, a small interruption causes cascading failures across your entire day. You miss a morning workout, skip meal prep, work late, and ruin your sleep. The system lacks circuit breakers.&lt;/p&gt;

&lt;p&gt;Here's a non-obvious implication of this shift. The best productivity hack is reducing your dependency graph. Look at your daily stack. How many third-party apps, habit trackers, and review loops do you run concurrently? Every extra tool is a point of failure. When your routine requires five different services just to figure out what to do next, you spend more time maintaining infrastructure than writing code.&lt;/p&gt;

&lt;p&gt;Software engineers understand that the most reliable code is the code you delete. The same rule applies to how you live. Strip out unnecessary dependencies in your daily schedule. Build fallback behaviors for when your energy crashes. If you can't run your full morning routine, what's the single fallback action that keeps the core process alive? Treat your lifestyle like a resilient distributed system, not a fragile script that panics when the database goes offline.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>selfimprovement</category>
      <category>lifestyle</category>
    </item>
    <item>
      <title>Building Secure Pipelines for Civic Technology</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Sat, 10 Oct 2026 15:28:10 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/building-secure-pipelines-for-civic-technology-11an</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/building-secure-pipelines-for-civic-technology-11an</guid>
      <description>&lt;p&gt;When a municipal water valve fails, the root cause is rarely malicious. More often, it traces back to an unverified config push that bypassed staging because the deployment pipeline dragged. As engineers building software for public institutions, our mandate goes way beyond clean code or snappy UI components. We are managing the digital nervous system of our communities, and our testing pipelines need to reflect that weight.&lt;/p&gt;

&lt;p&gt;Most commercial software can afford to break things and patch them later. Civic tech operates under a different social contract. If a voter registration database drops packets or a public transit routing API leaks location data, the damage strikes at the core of civic participation. Yet, many municipal contractors still treat security testing as an afterthought, bolted on right before production release when fixing vulnerabilities costs ten times more.&lt;/p&gt;

&lt;p&gt;Designing a resilient testing pipeline starts with treating infrastructure as code and enforcing zero trust principles at every commit. Practically, this means moving past basic unit tests and running automated policy checks locally before any code touches a shared repository. A solid local check scans container images for known CVEs, verifies that database migration scripts don't expose personally identifiable information, and validates API schemas against strict public sector compliance standards.&lt;/p&gt;

&lt;p&gt;Look at how we handle state management in public portals. A standard e-commerce site stores session tokens with minimal scrutiny. A municipal portal handling housing assistance applications must sanitize every incoming payload against hyper-specific regulatory frameworks. By embedding static analysis tools directly into pre-commit hooks, we catch boundary condition failures before code hits a pull request. &lt;/p&gt;

&lt;p&gt;Staging environments deserve the exact same governance as production systems. Developers often use degraded production dumps for testing without proper anonymization, which creates massive honeypots for bad actors. Synthetic data generation isn't just convenient; it's an obligation when dealing with citizen records. Build generators that mimic the statistical shape of real municipal traffic without containing actual citizen identifiers so staging becomes a genuinely safe proving ground.&lt;/p&gt;

&lt;p&gt;Technology is only as trustworthy as the systems we use to build it. If we want resilient public digital infrastructure, we have to start by making our engineering workflows transparent, accountable, and stubbornly secure.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>civic</category>
      <category>policy</category>
      <category>community</category>
    </item>
    <item>
      <title>Stop Building Microservices For A Monolith Problem</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Fri, 09 Oct 2026 13:17:16 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/stop-building-microservices-for-a-monolith-problem-530n</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/stop-building-microservices-for-a-monolith-problem-530n</guid>
      <description>&lt;p&gt;We love complexity because it feels like progress. Three weeks into a new SaaS build, half the engineering team is arguing about service meshes, gRPC contracts, and eventual consistency models for a database that currently holds zero paying users.&lt;/p&gt;

&lt;p&gt;This is a classic trap. Distributed systems solve organizational scaling friction. They aren't a badge of engineering maturity. When you slice a monolithic codebase into a dozen microservices before finding product-market fit, you pay a massive tax on your velocity for zero functional return.&lt;/p&gt;

&lt;p&gt;Look at what actually happens when a three-person team deploys a distributed architecture early. Instead of shipping features, they spend days debugging network partitions between an auth service and a user service. They write custom service discovery logic. They spend hours configuring CI pipelines to orchestrate deployments across multiple repositories. The cognitive load shifts away from solving the user problem and toward keeping a fragile web of internal APIs alive.&lt;/p&gt;

&lt;p&gt;At Kluvex, we learned this the hard way. Early on, we entertained the idea of splitting our core engine and our ingestion pipeline into separate services. It sounded clean. It sounded enterprise-ready. But every time we wanted to change a database schema, we had to coordinate deployments across three separate repos with overlapping dependency trees. We moved at a crawl.&lt;/p&gt;

&lt;p&gt;So we did the unglamorous thing. We collapsed the system back into a modular monolith. We kept domain boundaries strict inside a single codebase, using internal packages instead of network boundaries. If a function needed data from another domain, it called a method, not an HTTP endpoint. Serialization overhead vanished. Debugging became a matter of stepping through a single stack trace instead of tailing logs across five different container instances.&lt;/p&gt;

&lt;p&gt;The irony is that a well-structured monolith scales further than most people realize. You can run massive database instances, throw more memory at your single deployment, and optimize hot paths without worrying about distributed transactions or split-brain scenarios. You preserve the ability to move fast because your blast radius stays confined to a single process.&lt;/p&gt;

&lt;p&gt;When the time finally comes to extract a service, you will know. It won't be because a blog post told you to use event sourcing. It will happen because a specific bottleneck hammers your CPU or a distinct team needs independent deployment cycles to stop stepping on each other's toes. Until then, keep your code in one place, push features to production, and let your users tell you when you actually need the extra moving parts.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>developertools</category>
      <category>coding</category>
      <category>tech</category>
    </item>
    <item>
      <title>Building a Custom Grafana Panel for Circadian Biometrics</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Thu, 08 Oct 2026 12:54:03 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/building-a-custom-grafana-panel-for-circadian-biometrics-22o2</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/building-a-custom-grafana-panel-for-circadian-biometrics-22o2</guid>
      <description>&lt;p&gt;Most health dashboards treat time as a straight line. That works fine for server uptime, but it completely breaks down when you're trying to visualize human biology. Circadian rhythms are cyclical, messy, and personal. If you pull your sleep, heart rate variability, and continuous glucose data into standard time-series charts, you miss the actual phase shifts. You can't see the rhythm.&lt;/p&gt;

&lt;p&gt;I spent last weekend getting tired of default visualizations and built a custom Grafana panel plugin specifically for circadian rhythm anomalies. If you're a developer tracking your own biometrics or building internal tools for a health tech team, moving beyond standard line graphs is the only way to spot real patterns.&lt;/p&gt;

&lt;p&gt;Grafana supports custom panel development using React and the &lt;code&gt;@grafana/data&lt;/code&gt; and &lt;code&gt;@grafana/ui&lt;/code&gt; packages. The core architectural challenge here isn't fetching the data, though dealing with asynchronous time zones and daylight saving time transitions is its own headache. The real challenge is transformation. Standard dashboards expect metrics to increase or decrease over linear time. Circadian data needs a polar or 24-hour circular coordinate system to make sense of phase delays.&lt;/p&gt;

&lt;p&gt;To build this, you start by scaffolding a plugin using the Grafana tool frontend plugin command. Your panel component receives a data frame containing your timestamped metrics, such as body temperature troughs or resting heart rate spikes. Instead of plotting these against a standard X-axis, you map the timestamp modulo twenty-four hours to an angle on a 360-degree SVG canvas. The radius can represent the intensity of the metric or the date index, letting you see if your sleep onset creeps later across the weeks.&lt;/p&gt;

&lt;p&gt;Here's a snippet of the core time-to-angle transformation logic inside the React component:&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;DataFrame&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;@grafana/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CircadianProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DataFrame&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
 &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CircadianPanel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CircadianProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;No&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;found&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;time&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;valueField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&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;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeField&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;date&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;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&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;hours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMinutes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&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;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hours&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&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;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;valueField&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;svg&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;viewBox&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-100 -100 200 200&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;circle&lt;/span&gt; &lt;span class="nx"&gt;cx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;80&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;stroke&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#333&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;strokeWidth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;points&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&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;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&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;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;circle&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;cx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#5794F2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;})}&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/svg&lt;/span&gt;&lt;span class="err"&gt;&amp;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;Why does this technical detour matter for wellness and lifestyle engineering? Because the tooling you use shapes what you notice. When you view health data through a cyclic lens, you stop blaming yourself for daily fluctuations and start seeing systemic environmental friction. You spot the exact Wednesday night routine that triggers a phase delay, or the subtle seasonal shift in your core temperature minimum.&lt;/p&gt;

&lt;p&gt;The non-obvious implication of building your own biometric dashboards is ownership. Off-the-shelf fitness apps smooth out data into polite daily scores designed to keep you engaged through gamification. By writing your own panel plugins, you reclaim the raw telemetry. You stop consuming wellness as a finished product and start treating it as an open-source engineering problem.&lt;/p&gt;

</description>
      <category>wellness</category>
      <category>fitness</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Parsing Public Data: What Happens When Municipal APIs Break</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Wed, 07 Oct 2026 10:03:10 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/parsing-public-data-what-happens-when-municipal-apis-break-232f</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/parsing-public-data-what-happens-when-municipal-apis-break-232f</guid>
      <description>&lt;p&gt;When a municipal transit API drops coordinate fields at 4 AM, your ingestion script shouldn't kill the notification service. We spend weeks building streaming setups for commercial APIs, yet we treat government feeds like static CSVs. Public data powers everything from housing trackers to air quality monitors, but the infrastructure behind it is fragile. As engineers in civic tech, our job is to absorb that friction so users get reliable data.&lt;/p&gt;

&lt;p&gt;Working with city council voting records or spending databases brings specific headaches. Schemas shift because someone renamed a field in an old backend. Rate limits pop up out of nowhere because a server sits in a basement closet on decade-old hardware. Standard retries fail when endpoints return HTML error pages instead of JSON during a routine reboot. If your pipeline assumes consistency, it breaks.&lt;/p&gt;

&lt;p&gt;A robust civic data pipeline treats every payload as untrusted input. We run strict schema validation at the boundary and drop malformed records into a dead-letter queue instead of letting bad data poison downstream models. We write idempotent parsers so running an extraction script twice for a delayed tax assessment batch doesn't duplicate entries in the database. Defensive programming here is a basic requirement.&lt;/p&gt;

&lt;p&gt;Here is a Python fetch-and-parse routine that handles unexpected payload shifts without crashing your workers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_municipal_feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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="n"&gt;endpoint_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Network failure reaching civic endpoint: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

 &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Upstream server returned non-JSON payload, likely an error page.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

 &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSONDecodeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Malformed JSON received from municipal server.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This check stops common pipeline crashes caused by unexpected maintenance screens. Beyond code, our job involves transparency. When public data pipelines break, public trust in open government erodes. We monitor these ingestion jobs with the same rigor we apply to core telemetry and alert ourselves to stale datasets before anyone else notices the gap.&lt;/p&gt;

&lt;p&gt;Building better civic tech means accepting messy municipal infrastructure and writing code that bridges the gap between old systems and modern expectations.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>community</category>
      <category>policy</category>
      <category>civic</category>
    </item>
    <item>
      <title>Why Isolated Vector Database Benchmarks Are Useless</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Mon, 05 Oct 2026 18:38:27 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/why-isolated-vector-database-benchmarks-are-useless-1d0b</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/why-isolated-vector-database-benchmarks-are-useless-1d0b</guid>
      <description>&lt;p&gt;If you judge a vector database by its raw QPS on a static HNSW index, you are buying a sports car based entirely on how fast it rolls down a hill. Most published benchmarks test storage engines in sterile isolation. They load a million synthetic embeddings, run single-threaded nearest neighbor queries, and declare a winner.&lt;/p&gt;

&lt;p&gt;Real production systems do not work like this.&lt;/p&gt;

&lt;p&gt;In a real SaaS app backed by an LLM, vector search is rarely a solitary operation. It competes with write transactions, metadata filters, and connection pooling from multiple microservices. When a user queries your RAG pipeline, the system hits the database while concurrently updating user session states and ingesting new document chunks. Static benchmarks ignore the garbage collection pause that happens when memory pressure spikes during concurrent writes.&lt;/p&gt;

&lt;p&gt;Let us look at what actually breaks when you move from a benchmark script to production.&lt;/p&gt;

&lt;p&gt;First, filtering destroys index locality. Vector search engines love pure vector math. Add a basic metadata filter, such as restricting results to a specific tenant ID or date range, and the query planner has to choose between pre-filtering and post-filtering. Pre-filtering shreds recall if the subset is too small. Post-filtering fetches too few candidates to compute accurate similarities. Most benchmarks skip complex metadata filters entirely because it ruins their clean latency numbers.&lt;/p&gt;

&lt;p&gt;Second, memory mapping is not free. When your working set of vectors exceeds RAM, performance degrades rapidly. Benchmark scripts often fit entirely within cache. Real production workloads hit disk, triggering page faults that expose the underlying storage layer to latency spikes.&lt;/p&gt;

&lt;p&gt;Here is a simple python snippet demonstrating how a naive retrieval setup hides the cost of metadata filtering under load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;simulated_rag_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vectors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="c1"&gt;# Naive post-filtering: fetching top-k then filtering in application space
&lt;/span&gt; &lt;span class="n"&gt;raw_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="n"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw_results&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tenant&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="n"&gt;latency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;latency&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code looks harmless until concurrent threads flood the index. The database has to do extra work that the micro-benchmark never measured. If you rely on the database's internal filtering, the execution path shifts dramatically depending on selectivity.&lt;/p&gt;

&lt;p&gt;The non-obvious implication here is that your vector database choice matters less than your data layout and chunking strategy. A mediocre database with a well-partitioned namespace and aggressive caching will easily outperform an enterprise grade engine configured with default settings and a monolithic index.&lt;/p&gt;

&lt;p&gt;Stop reading synthetic benchmarks. Build a staging environment that mirrors your exact peak write-to-read ratio. Throw concurrent metadata updates at it, inject noisy background jobs, and measure the p99 latency under actual stress. That is the only benchmark that tells you whether your infrastructure will survive production.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>developertools</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Stop buying kitchen gadgets. Fix your vertical storage instead.</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Sun, 04 Oct 2026 16:01:49 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/stop-buying-kitchen-gadgets-fix-your-vertical-storage-instead-1jjj</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/stop-buying-kitchen-gadgets-fix-your-vertical-storage-instead-1jjj</guid>
      <description>&lt;p&gt;If your kitchen counters look like a battleground of coffee grinders and blenders, you don't need a bigger kitchen. You need a better layout.&lt;/p&gt;

&lt;p&gt;Counter space is prime real estate. When gadgets take over, your actual workspace shrinks, forcing you to prep meals around clumsy obstacles. Instead of blowing your budget on another countertop appliance, look up. Moving your daily essentials to the walls changes how a kitchen functions.&lt;/p&gt;

&lt;p&gt;Think about your kitchen like a code repository. If your main branch—your prep surface—is constantly blocked by bulky toasters and knife blocks, nothing gets built efficiently. Vertical storage acts like a refactoring for your cooking routine. When you mount spice racks, utensil rails, and shelving directly to the wall, you pull clutter off the horizontal plane and open up square footage.&lt;/p&gt;

&lt;p&gt;Modular units work well here. Grab a narrow shelf or cube storage unit, slide it into an awkward gap near your counter, and gain a dedicated coffee station. The trick is keeping footprints minimal while maximizing vertical height. Stack items logically based on how often you reach for them instead of letting things spread out horizontally.&lt;/p&gt;

&lt;p&gt;Mounting things in a kitchen carries risks. If you rent, or just hate patching drywall, drilling holes for every small spice rack is a headache. Pressure-sensitive adhesive strips let you hang lightweight organizers securely without tearing up tiles. Check weight ratings carefully. Steam, grease, and heat near the stove weaken adhesives over time, so keep heavy cast iron pans on sturdy wall-mounted brackets and reserve strips for lighter tools.&lt;/p&gt;

&lt;p&gt;Stop treating counters as permanent storage. Use your walls, and meal prep stops feeling like an obstacle course.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>lifestyle</category>
      <category>selfimprovement</category>
    </item>
    <item>
      <title>Hosting Open-Source Civic Tech Without Compromising Public Trust</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Sat, 03 Oct 2026 15:23:35 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/hosting-open-source-civic-tech-without-compromising-public-trust-3eok</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/hosting-open-source-civic-tech-without-compromising-public-trust-3eok</guid>
      <description>&lt;p&gt;Municipalities run public portals, voting tools, and budget trackers on open-source repos. Building government software for years taught me one thing: standard dev workflows clash hard with public infrastructure security. We treat civic code like SaaS apps and forget public-sector data has entirely different threat vectors.&lt;/p&gt;

&lt;p&gt;Open civic frameworks enable transparent auditing. Watchdogs and citizens check the source code for fairness in algorithms or municipal spending. Publishing that code also hands attackers an exploit blueprint. When a vulnerability drops in a public repo, automated scanners hit exposed servers in hours instead of months.&lt;/p&gt;

&lt;p&gt;Securing these deployments means ditching perimeter defense for zero-trust. If you maintain a civic repo, code quality isn't your only worry. You need to ensure stolen contributor creds can't push malicious payloads into production builds.&lt;/p&gt;

&lt;p&gt;Here is what that looks like in a standard CI pipeline for municipal apps. We isolate build runners, enforce container image signing, and lock down network egress. Check this baseline GitHub Actions workflow that signs build artifacts before they hit public registries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Secure Civic Build&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;build-and-sign&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
 &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
 &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

 &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Source&lt;/span&gt;
 &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
 &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Push Container&lt;/span&gt;
 &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v5&lt;/span&gt;
 &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
 &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
 &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.civic.gov/app:latest&lt;/span&gt;
 &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=image,name=target,annotation-index.org.opencontainers.image.source=${{ github.repository }}&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sign Artifact&lt;/span&gt;
 &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
 &lt;span class="s"&gt;cosign sign --yes registry.civic.gov/app:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crypto doesn't fix the human side of civic tech. Municipal IT departments run on tight budgets and thin staff. They use third-party hosts or cloud services that often leave storage buckets misconfigured with sensitive citizen data. A public budgeting tool leaks personal info the second database permissions match standard dev defaults.&lt;/p&gt;

&lt;p&gt;This friction causes shadow civic IT. When official channels move too slowly, well-meaning devs spin up unvetted open-source instances on personal cloud accounts. That leaves fragmented data silos outside public records laws and oversight boards.&lt;/p&gt;

&lt;p&gt;Our job goes beyond clean functions and passing tests. When we write civic infrastructure, we build the digital public square. Treat deployment pipelines as critical public utilities that demand strict auditing and threat modeling.&lt;/p&gt;

</description>
      <category>technology</category>
      <category>civic</category>
      <category>policy</category>
      <category>community</category>
    </item>
    <item>
      <title>Why Dev Teams Still Fight Over Semicolons</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Fri, 02 Oct 2026 12:40:31 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/why-dev-teams-still-fight-over-semicolons-1nak</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/why-dev-teams-still-fight-over-semicolons-1nak</guid>
      <description>&lt;p&gt;Every engineering team loses an afternoon to a religious war over code style. Tabs versus spaces. Trailing commas. Brace placement. Seniors roll their eyes while juniors take it personally, and leadership wonders why ticket velocity drops during refactoring sprints. &lt;/p&gt;

&lt;p&gt;We pretend these arguments are about readability. We cite studies and compiler performance, but we lie to ourselves. Code style debates are proxy wars for cognitive load.&lt;/p&gt;

&lt;p&gt;Here is what happens under the hood. Human memory is associative, and reading code requires holding a mental model of execution state in working memory. When you read code written in a style that diverges from your internal heuristics, your brain spends cycles on translation. It reads like a foreign language written in a familiar alphabet. You parse the words, but you cannot skim for meaning without friction.&lt;/p&gt;

&lt;p&gt;Now layer artificial intelligence into this dynamic. Modern codebases are hybrid creations born half from human intent and half from autocomplete models trained on the public internet. Every time an LLM injects a block of TypeScript into your editor, it brings its own stylistic consensus. Sometimes it writes idiomatic modern syntax. Sometimes it dredges up patterns from ancient repositories.&lt;/p&gt;

&lt;p&gt;This introduces a new source of cognitive noise. We used to fight because humans have distinct signatures. Now we fight because our tools have zero taste, churning out code that lacks internal stylistic consistency.&lt;/p&gt;

&lt;p&gt;When a pull request triggers a massive debate about naming conventions or structural layout, the team tries to impose order on chaos. They attempt to lower the cognitive barrier to entry for whoever debugs that function at three in the morning.&lt;/p&gt;

&lt;p&gt;Smart teams stopped fighting about this manually years ago. They automated the fix with strict linters and formatters running on every commit. If the machine enforces a single standard, human variance drops to zero, and emotional energy goes back where it belongs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;linter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;does&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;care&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;about&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;feelings,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tree.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"semi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"trailingComma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"es5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"singleQuote"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"printWidth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with formatters, style wars persist because formatting is only skin-deep. Deeper disagreements involve architectural patterns, modularity, and error handling philosophy. These resist config files.&lt;/p&gt;

&lt;p&gt;When an AI assistant suggests a monolithic function spanning two hundred lines with deeply nested conditionals, it violates maintainable software engineering. Accepting that code saves time today and costs sanity tomorrow. Pushing back feels like pedantry, but it is self-defense.&lt;/p&gt;

&lt;p&gt;We fight over code style because code is our primary medium of thought. If the syntax is messy, our thinking becomes messy. As tooling grows powerful, keeping codebases clean is the only way to remain the authors of our systems instead of their editors.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>developertools</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
    <item>
      <title>Local-First Heart Rate Tracking: A Database Schema Story</title>
      <dc:creator>Puneet Khandelwal</dc:creator>
      <pubDate>Thu, 01 Oct 2026 09:30:10 +0000</pubDate>
      <link>https://dev.to/puneet_khandelwal_429a72e/local-first-heart-rate-tracking-a-database-schema-story-54d7</link>
      <guid>https://dev.to/puneet_khandelwal_429a72e/local-first-heart-rate-tracking-a-database-schema-story-54d7</guid>
      <description>&lt;p&gt;Your wearable is leaking your pulse to a server you don't own. &lt;/p&gt;

&lt;p&gt;Every time you run, a Bluetooth Low Energy peripheral broadcasts your cardiac rhythm into a proprietary black box. I got tired of export buttons that take three business days to email a zip file of CSVs, so I spent a weekend building a local-first pipeline for my fitness data. &lt;/p&gt;

&lt;p&gt;Heart rate telemetry is just numbers over time. Yet, standard relational databases choke on the write amplification when you ingest one sample every single second. Treat a continuous stream like a standard CRUD table and your disk IO spikes while queries crawl.&lt;/p&gt;

&lt;p&gt;The fix borrows a page from industrial IoT engineers. Instead of appending raw rows indefinitely, I designed a SQLite schema that relies on rigid time-bucket partitioning. Active workout data stays in a rolling in-memory buffer before flushing fixed five-minute chunks into compressed binary blobs on disk. SQLite handles the file management, and a lightweight custom parser runs the delta compression.&lt;/p&gt;

&lt;p&gt;Here's the core schema design that keeps my local dashboard snappy, even with millions of raw samples:&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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;workouts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;started_at&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;sport_type&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;heart_rate_chunks&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="n"&gt;workout_id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;bucket_epoch&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;sample_count&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="n"&gt;compressed_deltas&lt;/span&gt; &lt;span class="nb"&gt;BLOB&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workout_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_epoch&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workout_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;workouts&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Storing small integer deltas instead of absolute timestamps and raw beats-per-minute integers shrinks the storage footprint hard. A two-hour marathon trace fits comfortably inside a file smaller than a high-resolution JPEG. More importantly, I can run local analytics without my laptop fan spinning up to jet-engine speeds.&lt;/p&gt;

&lt;p&gt;Building your own health stack forces you to confront how little control we have over our most intimate metrics. When your wellness data lives on your own hardware, you stop looking at fitness as a streak to maintain for an app badge and start treating it as a personal dataset to actually understand.&lt;/p&gt;

</description>
      <category>fitness</category>
      <category>health</category>
      <category>selfimprovement</category>
    </item>
  </channel>
</rss>
