<?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: Nikita Volkov</title>
    <description>The latest articles on DEV Community by Nikita Volkov (@nikitavolkov).</description>
    <link>https://dev.to/nikitavolkov</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%2F192444%2F5c19a595-82d5-41d7-b003-4f625b8f5107.png</url>
      <title>DEV Community: Nikita Volkov</title>
      <link>https://dev.to/nikitavolkov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikitavolkov"/>
    <language>en</language>
    <item>
      <title>My 14-Year Journey Away from ORMs - How I Built pGenie, the SQL-First Postgres Code Generator</title>
      <dc:creator>Nikita Volkov</dc:creator>
      <pubDate>Tue, 14 Apr 2026 09:47:03 +0000</pubDate>
      <link>https://dev.to/nikitavolkov/my-14-year-journey-away-from-orms-how-i-built-pgenie-the-sql-first-postgres-code-generator-5c3j</link>
      <guid>https://dev.to/nikitavolkov/my-14-year-journey-away-from-orms-how-i-built-pgenie-the-sql-first-postgres-code-generator-5c3j</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 1 of "pGenie in Production" - the real story behind the open-source tool that finally makes your database the single source of truth.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hi, I'm Nikita Volkov - architect, consultant, and the author of &lt;code&gt;hasql&lt;/code&gt;, one of the two main PostgreSQL drivers in Haskell used in major production projects like PostgREST and IHP. After 25 years in IT and more late-night schema-drift fires than I care to count, I open-sourced something I wish had existed a decade ago: &lt;strong&gt;&lt;a href="https://pgenie.io?utm_source=devto&amp;amp;utm_campaign=series-part1" rel="noopener noreferrer"&gt;pGenie&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Following is the story of how I went from shipping a popular ORM in 2012… to throwing it all away… to realizing that the database itself should be the single source of truth.&lt;/p&gt;

&lt;h3&gt;
  
  
  2012: I Built an ORM
&lt;/h3&gt;

&lt;p&gt;Back then I was a typical web developer of the time. CRUD was all we needed. Performance problems were for "later". Deployments were &lt;code&gt;ssh&lt;/code&gt; + &lt;code&gt;git pull&lt;/code&gt;. Migrations were non-existent.&lt;/p&gt;

&lt;p&gt;So I wrote &lt;strong&gt;SORM&lt;/strong&gt; - an ORM framework for Scala - and open-sourced it. Instead of treating objects as mutable views of the database, it exchanged them as immutable values with the db. It felt novel. It gained traction. For a while it felt like success.&lt;/p&gt;

&lt;p&gt;Then reality hit.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every non-trivial query exposed the limits of the DSL.
&lt;/li&gt;
&lt;li&gt;Keeping the code model in sync with the real database schema became a headache.
&lt;/li&gt;
&lt;li&gt;I was essentially inventing a worse version of SQL just to make the integration "safer".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2014 I stopped development. In 2016 I killed support. Lesson learned: &lt;strong&gt;abstracting &lt;em&gt;over&lt;/em&gt; SQL is usually a mistake&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insight #1: Instead of replacing SQL - we should make integration with it better
&lt;/h3&gt;

&lt;p&gt;Instead of building another layer on top, I decided to make the integration with raw SQL as safe and convenient as possible.&lt;/p&gt;

&lt;p&gt;That led to &lt;strong&gt;hasql&lt;/strong&gt; in 2014 - a driver that embraced raw SQL and gave composable abstractions &lt;em&gt;supporting&lt;/em&gt; it. It's one of the two main Postgres drivers in the Haskell ecosystem today and the most efficient one.&lt;/p&gt;

&lt;p&gt;With this library I found the perfect integration point.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insight #2: The Query &lt;em&gt;Is&lt;/em&gt; the Integration Point
&lt;/h3&gt;

&lt;p&gt;Not the application model like &lt;code&gt;User&lt;/code&gt;. Not the table row structure. &lt;strong&gt;The query itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The query determines the structure of parameters it needs and what it returns. Trying to reuse "row types" across queries only creates hidden dependencies and future breakage.&lt;/p&gt;

&lt;p&gt;That perspective made a solid foundation, but something was still missing.&lt;/p&gt;

&lt;p&gt;The user had to accompany every query with hand-rolled codecs and the SQL string was lacking any validation. So in 2019 I shipped &lt;strong&gt;hasql-th&lt;/strong&gt; - a compile-time SQL syntax checking library based on a port of the PostgreSQL parser. It made things simpler and safer, but still not enough. We needed the queries validated &lt;em&gt;against the actual schema&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I saw projects pop up over the years that tried to solve this problem by introducing a compile-time connection to the database. But that always felt like a hack that made builds unreproducible. I was looking for a solution that could be contained in code in a repository without infrastructure dependencies, and eventually it came in the form of the following insight.&lt;/p&gt;

&lt;h3&gt;
  
  
  Insight #3: Migrations + Queries = Database API
&lt;/h3&gt;

&lt;p&gt;Here's the mental model that changed everything for me:&lt;/p&gt;

&lt;p&gt;If you treat your Postgres server like a microservice, then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrations = the contract (like OpenAPI schema for REST).
&lt;/li&gt;
&lt;li&gt;Queries = the operations.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your application is just a &lt;em&gt;client&lt;/em&gt; of that API. The database is primary. The application code is secondary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the DB-First (or SQL-First) approach.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Suddenly everything clicked. The schema lives in SQL migrations. The queries live in &lt;code&gt;.sql&lt;/code&gt; files. All of that gets checked into a repository and becomes a subject to development, testing and review. Everything else - types, encoders, decoders, index recommendations - can be &lt;em&gt;automatically derived&lt;/em&gt; and safely integrated into codebases with the help of type-checking compilers.&lt;/p&gt;

&lt;h3&gt;
  
  
  2022–2026: From Closed SaaS Experiment to Open Source
&lt;/h3&gt;

&lt;p&gt;I first released pGenie as SaaS in 2022. Companies didn't like sending their schemas and queries to a third party. Fair enough.&lt;/p&gt;

&lt;p&gt;So in 2026 I open-sourced it completely and switched to the OSS+Consulting business model:  &lt;/p&gt;

&lt;p&gt;→ &lt;strong&gt;&lt;a href="https://github.com/pgenie-io/pgenie" rel="noopener noreferrer"&gt;github.com/pgenie-io/pgenie&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a simple CLI. You point it at your &lt;code&gt;migrations/&lt;/code&gt; and &lt;code&gt;queries/&lt;/code&gt; folders. It spins up a real PostgreSQL container, recreates the schema by running migrations, prepares queries, analyses the information schema, and generates 100% type-safe client code for various targets via plugins. Currently Haskell, Rust and Java are fully supported as such plugins.&lt;/p&gt;

&lt;h3&gt;
  
  
  What pGenie Actually Gives You
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Zero boilerplate type-safe SDKs&lt;/li&gt;
&lt;li&gt;Fidelity to advanced Postgres features (JSONB, arrays, composites, multiranges, extensions, etc.) and queries of any complexity&lt;/li&gt;
&lt;li&gt;Decentralized code generators written in safe, reproducible Dhall&lt;/li&gt;
&lt;li&gt;Signature files that catch schema drift &lt;em&gt;at build time&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Automatic index recommendations and sequential-scan warnings
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full side-by-side of source SQL and the generated client code for Haskell, Rust and Java is in &lt;a href="https://github.com/pgenie-io/demo" rel="noopener noreferrer"&gt;the demo repo&lt;/a&gt; — try it yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters Now (Especially in the AI Era)
&lt;/h3&gt;

&lt;p&gt;LLMs are fantastic at writing SQL. They are not that great at ensuring it still works after your next migration.&lt;/p&gt;

&lt;p&gt;pGenie closes that gap: AI writes the query → pGenie verifies it against the real schema → you get a predictably structured type-safe SDK in seconds.&lt;/p&gt;

&lt;p&gt;SQL-First + AI = maximum velocity with zero surprises.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ready to stop fighting your database?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Study the docs: &lt;a href="https://pgenie.io/docs?utm_source=devto&amp;amp;utm_campaign=series-part1" rel="noopener noreferrer"&gt;pgenie.io/docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ask questions and share ideas: &lt;a href="https://github.com/pgenie-io/pgenie/discussions" rel="noopener noreferrer"&gt;github.com/pgenie-io/pgenie/discussions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Give it a ⭐ on GitHub: &lt;a href="https://github.com/pgenie-io/pgenie" rel="noopener noreferrer"&gt;github.com/pgenie-io/pgenie&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>java</category>
      <category>rust</category>
      <category>haskell</category>
    </item>
  </channel>
</rss>
