<?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: Mohammad</title>
    <description>The latest articles on DEV Community by Mohammad (@calligra).</description>
    <link>https://dev.to/calligra</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%2F1537554%2Fb2c296b3-3398-4ad8-9892-752bdb69602a.png</url>
      <title>DEV Community: Mohammad</title>
      <link>https://dev.to/calligra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/calligra"/>
    <language>en</language>
    <item>
      <title>Listening to the Heartbeat of Your Database: Understanding Change Data Capture (CDC)</title>
      <dc:creator>Mohammad</dc:creator>
      <pubDate>Fri, 19 Jun 2026 12:06:56 +0000</pubDate>
      <link>https://dev.to/calligra/listening-to-the-heartbeat-of-your-database-understanding-change-data-capture-cdc-4kib</link>
      <guid>https://dev.to/calligra/listening-to-the-heartbeat-of-your-database-understanding-change-data-capture-cdc-4kib</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;CDC stands for &lt;strong&gt;Change Data Capture&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What You Will Learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What CDC is&lt;/li&gt;
&lt;li&gt;The story behind it&lt;/li&gt;
&lt;li&gt;How it works internally&lt;/li&gt;
&lt;li&gt;Common use cases&lt;/li&gt;
&lt;li&gt;A simplified implementation example&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  What Is CDC?
&lt;/h1&gt;

&lt;p&gt;Change Data Capture (CDC) is a technique for capturing changes made to data and exposing those changes to external systems.&lt;/p&gt;

&lt;p&gt;Instead of repeatedly querying a database to check whether something has changed, CDC allows applications to consume a stream of inserts, updates, and deletes as they happen.&lt;/p&gt;

&lt;p&gt;Think of CDC as a way to listen to the heartbeat of your database.&lt;/p&gt;

&lt;p&gt;Every time data changes, the database records that change internally. CDC makes those changes available so that other systems can react to them in real time.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Story Behind CDC
&lt;/h1&gt;

&lt;p&gt;To understand CDC, it helps to understand how databases have worked for decades.&lt;/p&gt;

&lt;p&gt;Whenever data changes, databases do not simply overwrite the old value and move on. They maintain an internal record of operations that have occurred.&lt;/p&gt;

&lt;p&gt;These records are commonly stored in transaction logs.&lt;/p&gt;

&lt;p&gt;Historically, every database implemented these logs differently, but the purpose was always the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record every change made to the database&lt;/li&gt;
&lt;li&gt;Recover from failures&lt;/li&gt;
&lt;li&gt;Replicate data to other nodes&lt;/li&gt;
&lt;li&gt;Maintain consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every insert, update, and delete operation generates a log entry.&lt;/p&gt;

&lt;p&gt;These entries are ordered and identified by a unique position or token.&lt;/p&gt;

&lt;p&gt;The ordering is critical.&lt;/p&gt;

&lt;p&gt;Imagine the following sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create User&lt;/li&gt;
&lt;li&gt;Update User&lt;/li&gt;
&lt;li&gt;Delete User&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a replica applied these operations in a different order, it would end up with completely different data.&lt;/p&gt;

&lt;p&gt;Because these logs are ordered, databases can reliably replay changes and reconstruct state over time.&lt;/p&gt;

&lt;p&gt;This is how replication works in many database systems.&lt;/p&gt;

&lt;p&gt;A replica typically starts with an initial snapshot and then continuously applies new changes by replaying the transaction log.&lt;/p&gt;




&lt;h1&gt;
  
  
  Where CDC Comes In
&lt;/h1&gt;

&lt;p&gt;Originally, these logs were intended for the database itself.&lt;/p&gt;

&lt;p&gt;CDC extends that idea by allowing external applications to consume those same changes.&lt;/p&gt;

&lt;p&gt;Instead of only replicas reading database changes, your applications can read them too.&lt;/p&gt;

&lt;p&gt;This means that whenever data changes, you can react immediately without constantly querying the database.&lt;/p&gt;

&lt;p&gt;The underlying idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Expose an ordered stream of database changes that external systems can consume reliably.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  How CDC Works
&lt;/h1&gt;

&lt;p&gt;Different databases implement CDC differently, but the concept remains the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  MongoDB
&lt;/h3&gt;

&lt;p&gt;MongoDB provides CDC through &lt;strong&gt;Change Streams&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each event contains a &lt;strong&gt;resume token&lt;/strong&gt;, which allows consumers to continue reading from the last processed change after a restart or failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  MySQL
&lt;/h3&gt;

&lt;p&gt;MySQL exposes changes through its &lt;strong&gt;Binary Log (Binlog)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;CDC tools can read the binlog and transform database operations into events.&lt;/p&gt;

&lt;h3&gt;
  
  
  PostgreSQL
&lt;/h3&gt;

&lt;p&gt;PostgreSQL provides CDC through &lt;strong&gt;Logical Decoding&lt;/strong&gt; and &lt;strong&gt;Logical Replication&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These mechanisms allow applications to consume database changes in order.&lt;/p&gt;

&lt;p&gt;Although the implementations differ, they all provide the same capability:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An ordered stream of database changes.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  CDC Is Usually Pull-Based
&lt;/h1&gt;

&lt;p&gt;One common misconception is that databases push changes directly to your application.&lt;/p&gt;

&lt;p&gt;In reality, most CDC implementations are fundamentally pull-based.&lt;/p&gt;

&lt;p&gt;The consumer requests the next available change from the database.&lt;/p&gt;

&lt;p&gt;However, applications typically maintain a long-lived connection or cursor, making the experience feel very similar to receiving pushed events.&lt;/p&gt;

&lt;p&gt;For example, MongoDB's &lt;code&gt;watch()&lt;/code&gt; API keeps a stream open and continuously delivers new events as they become available.&lt;/p&gt;

&lt;p&gt;From the application's perspective, it feels real-time.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simplified CDC Example
&lt;/h1&gt;

&lt;p&gt;To understand the concept, imagine that every database change is converted into a standardized event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;captureDataChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$after&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$cdcEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"metadata"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s2"&gt;"operation"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$operation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"table"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s2"&gt;"before"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"after"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$after&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Streaming event: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cdcEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;captureDataChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"UPDATE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"role"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Developer"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"role"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Architect"&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;Output:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&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;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UPDATE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;42&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;"before"&lt;/span&gt;&lt;span class="p"&gt;:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Developer"&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;"after"&lt;/span&gt;&lt;span class="p"&gt;:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Architect"&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="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;Real CDC systems work differently under the hood, but the idea is very similar:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect a database change&lt;/li&gt;
&lt;li&gt;Convert it into an event&lt;/li&gt;
&lt;li&gt;Publish it to interested consumers&lt;/li&gt;
&lt;/ol&gt;




&lt;h1&gt;
  
  
  CDC vs Traditional Polling
&lt;/h1&gt;

&lt;p&gt;Without CDC, applications often use polling.&lt;/p&gt;

&lt;p&gt;For example:&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;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;last_seen_timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach works, but it has drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Additional load on the database&lt;/li&gt;
&lt;li&gt;Expensive queries at scale&lt;/li&gt;
&lt;li&gt;Possibility of missing updates&lt;/li&gt;
&lt;li&gt;Duplicate processing logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CDC avoids these problems by consuming changes directly from the database's change stream.&lt;/p&gt;

&lt;p&gt;Instead of repeatedly asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Has anything changed?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;the database tells you:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"This is what changed."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Why CDC Matters
&lt;/h1&gt;

&lt;p&gt;CDC is more than a database feature.&lt;/p&gt;

&lt;p&gt;It is an architectural building block.&lt;/p&gt;

&lt;p&gt;Once database changes become events, many new possibilities emerge.&lt;/p&gt;

&lt;p&gt;Common use cases include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search index synchronization (Elasticsearch/OpenSearch)&lt;/li&gt;
&lt;li&gt;Analytics pipelines&lt;/li&gt;
&lt;li&gt;Event-driven architectures&lt;/li&gt;
&lt;li&gt;Cache invalidation&lt;/li&gt;
&lt;li&gt;Audit trails&lt;/li&gt;
&lt;li&gt;Data warehouse synchronization&lt;/li&gt;
&lt;li&gt;Building materialized views&lt;/li&gt;
&lt;li&gt;Synchronizing data across services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, when a product is updated in your database, a CDC consumer can automatically update:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search indexes&lt;/li&gt;
&lt;li&gt;Caches&lt;/li&gt;
&lt;li&gt;Reporting systems&lt;/li&gt;
&lt;li&gt;Recommendation engines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;without requiring changes to the original application.&lt;/p&gt;




&lt;h1&gt;
  
  
  CDC in Modern Architectures
&lt;/h1&gt;

&lt;p&gt;Many organizations use CDC platforms such as Debezium to provide a unified way of consuming changes across different databases.&lt;/p&gt;

&lt;p&gt;These platforms often publish CDC events into systems such as Apache Kafka, where multiple consumers can process the same stream independently.&lt;/p&gt;

&lt;p&gt;This makes CDC a key building block in modern event-driven systems.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;At its core, CDC is a simple idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every database change is already being recorded somewhere. CDC allows external systems to listen to those changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whether it is MongoDB Change Streams, MySQL Binlogs, or PostgreSQL Logical Replication, the principle remains the same:&lt;/p&gt;

&lt;p&gt;An ordered stream of database changes that can be consumed reliably.&lt;/p&gt;

&lt;p&gt;Once you start viewing database updates as events rather than rows being modified, CDC becomes one of the most powerful tools for building scalable and loosely coupled systems.&lt;/p&gt;

</description>
      <category>cdc</category>
      <category>database</category>
      <category>architecture</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>From Notion to Everywhere: Building Calligra</title>
      <dc:creator>Mohammad</dc:creator>
      <pubDate>Thu, 18 Jun 2026 18:11:58 +0000</pubDate>
      <link>https://dev.to/calligra/from-notion-to-everywhere-building-calligra-9p6</link>
      <guid>https://dev.to/calligra/from-notion-to-everywhere-building-calligra-9p6</guid>
      <description>&lt;p&gt;Every developer blog needs a first post.&lt;/p&gt;

&lt;p&gt;Mine starts with a pen, a family name, and a workflow I got tired of doing by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Publishing Problem
&lt;/h2&gt;

&lt;p&gt;I write almost everything in Notion.&lt;/p&gt;

&lt;p&gt;It is where ideas begin, drafts evolve, and articles eventually take shape. But publishing those articles was always the same repetitive process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy the content from Notion.&lt;/li&gt;
&lt;li&gt;Fix formatting issues.&lt;/li&gt;
&lt;li&gt;Paste it into my blog.&lt;/li&gt;
&lt;li&gt;Repeat for &lt;a href="http://dev.to/"&gt;Dev.to&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Repeat for Hashnode.&lt;/li&gt;
&lt;li&gt;Repeat for Medium.&lt;/li&gt;
&lt;li&gt;Upload cover images multiple times.&lt;/li&gt;
&lt;li&gt;Configure canonical URLs manually.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of these steps were difficult.&lt;/p&gt;

&lt;p&gt;They were simply repetitive.&lt;/p&gt;

&lt;p&gt;And as developers, repetitive work usually means there is an opportunity for automation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Goal
&lt;/h2&gt;

&lt;p&gt;I wanted a publishing workflow that looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Notion → Calligra → Personal Blog + Dev.to + Hashnode + Medium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write once.&lt;/p&gt;

&lt;p&gt;Publish everywhere.&lt;/p&gt;

&lt;p&gt;Keep my own domain as the source of truth.&lt;/p&gt;

&lt;p&gt;That simple idea became Calligra.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Calligra?
&lt;/h2&gt;

&lt;p&gt;Calligra is an open-source CLI tool that takes content from Notion and publishes it to multiple destinations with a single command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nb"&gt;sync
&lt;/span&gt;make build
make publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, Calligra:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetches content from the Notion API&lt;/li&gt;
&lt;li&gt;Converts the content into platform-specific formats&lt;/li&gt;
&lt;li&gt;Uploads images and assets&lt;/li&gt;
&lt;li&gt;Publishes to supported platforms&lt;/li&gt;
&lt;li&gt;Sets canonical URLs back to your own website&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your personal blog owns the content. Other platforms help distribute it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Screenshot: Publishing Workflow
&lt;/h2&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb38d05ls8joavmpvm05b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb38d05ls8joavmpvm05b.png" alt="Blog Asset" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────┐
│  Notion  │
└────┬─────┘
     │
     ▼
┌──────────┐
│ Calligra │
└────┬─────┘
     │
     ├──► Personal Blog
     ├──► Dev.to
     ├──► Hashnode
     └──► Medium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Story Behind the Name
&lt;/h2&gt;

&lt;p&gt;Before the code, there is the name.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Craft
&lt;/h3&gt;

&lt;p&gt;The word &lt;em&gt;Calligra&lt;/em&gt; comes from the same roots as &lt;em&gt;calligraphy&lt;/em&gt; — the art of beautiful writing.&lt;/p&gt;

&lt;p&gt;Calligraphy is not just about words.&lt;/p&gt;

&lt;p&gt;It is about intention, structure, and craftsmanship.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Heritage
&lt;/h3&gt;

&lt;p&gt;My family name is &lt;strong&gt;Ghalambaz (قلم‌باز)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A rough translation would be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Pen player."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Historically, it referred to someone who practiced writing as both a craft and an art.&lt;/p&gt;

&lt;p&gt;Someone who enjoyed working with words.&lt;/p&gt;

&lt;p&gt;When I built a tool dedicated entirely to writing and publishing, the name felt inevitable.&lt;/p&gt;

&lt;p&gt;Calligra connects a modern publishing workflow with a much older tradition of writing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open Source from Day One
&lt;/h2&gt;

&lt;p&gt;Although Calligra started as a personal tool, I built it to be useful for other developers as well.&lt;/p&gt;

&lt;p&gt;If you write in Notion or anywhere else and publish technical content, you might find it useful.&lt;/p&gt;

&lt;p&gt;The project is open source, and contributions, feedback, and ideas are always welcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt;  &lt;a href="https://github.com/ghalambaz/Calligra" rel="noopener noreferrer"&gt;&lt;strong&gt;Ghalambaz/Calligra&lt;/strong&gt;&lt;/a&gt; &lt;/p&gt;




&lt;h2&gt;
  
  
  Why Start This Blog?
&lt;/h2&gt;

&lt;p&gt;Calligra solves the publishing problem.&lt;/p&gt;

&lt;p&gt;Now it's time to focus on the writing.&lt;/p&gt;

&lt;p&gt;This blog will be a place for practical engineering lessons, architectural decisions, debugging stories, and lessons learned from building software in production.&lt;/p&gt;

&lt;p&gt;Not tutorials copied from documentation.&lt;/p&gt;

&lt;p&gt;Not generic AI-generated content.&lt;/p&gt;

&lt;p&gt;I do use AI—but as an editor, not an author.&lt;/p&gt;

&lt;p&gt;AI is great at fact-checking, catching mistakes, improving clarity, challenging assumptions, and helping me communicate ideas more effectively. The experiences, opinions, successes, and failures will still be my own.&lt;/p&gt;

&lt;p&gt;The goal is simple: share real engineering stories, real production problems, and solutions that might help someone else avoid the same mistakes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This post marks the beginning of the journey, not the destination.&lt;/p&gt;

&lt;p&gt;Over time, I'll be writing about software architecture, backend engineering, distributed systems, debugging sessions, performance bottlenecks, deployment lessons, and the occasional mistake that taught me something valuable.&lt;/p&gt;

&lt;p&gt;Some articles will be technical deep dives.&lt;/p&gt;

&lt;p&gt;Some will be lessons learned from real projects.&lt;/p&gt;

&lt;p&gt;Some may simply document a problem that took far longer to solve than it should have.&lt;/p&gt;

&lt;p&gt;The goal is not to publish content for the sake of publishing.&lt;/p&gt;

&lt;p&gt;The goal is to share experiences, capture lessons before they are forgotten, and hopefully help other engineers who run into similar challenges along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Calligra started as a way to eliminate a repetitive workflow.&lt;/p&gt;

&lt;p&gt;Now it has become the foundation of this blog.&lt;/p&gt;

&lt;p&gt;If you are a developer who writes in Notion and owns a personal domain, I hope it saves you time as well.&lt;/p&gt;

&lt;p&gt;Thanks for stopping by.&lt;/p&gt;

&lt;p&gt;See you in the next post.&lt;/p&gt;

&lt;p&gt;—&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mohammad Ghalambaz&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;writing at Calligra.dev&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blogging</category>
      <category>developerworkflow</category>
      <category>publishing</category>
    </item>
  </channel>
</rss>
