<?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: Sina Mohammadi</title>
    <description>The latest articles on DEV Community by Sina Mohammadi (@mohammadi).</description>
    <link>https://dev.to/mohammadi</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%2F294309%2F05be4717-45b0-4b3c-a528-476d22730f4b.png</url>
      <title>DEV Community: Sina Mohammadi</title>
      <link>https://dev.to/mohammadi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohammadi"/>
    <language>en</language>
    <item>
      <title>The Customer Who Said "Delete Me" (and the 420 Million Events That Said "No")</title>
      <dc:creator>Sina Mohammadi</dc:creator>
      <pubDate>Mon, 19 Oct 2026 07:47:04 +0000</pubDate>
      <link>https://dev.to/mohammadi/the-customer-who-said-delete-me-and-the-420-million-events-that-said-no-20o</link>
      <guid>https://dev.to/mohammadi/the-customer-who-said-delete-me-and-the-420-million-events-that-said-no-20o</guid>
      <description>&lt;p&gt;&lt;em&gt;A system design story about GDPR, event sourcing, and a very long Tuesday.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;It was a normal Tuesday. The coffee was fine. The build was green. Nothing was on fire.&lt;/p&gt;

&lt;p&gt;Then Legal sent me a Slack message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Hey, quick one 🙂 A user sent a GDPR erasure request. We have 30 days to answer. It's been 20. Can you delete her data? Should be easy, right?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I read it three times.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Twenty days.&lt;/em&gt; The request had been sitting in a shared inbox for twenty days. Nobody's fault. Everybody's fault. That's how shared inboxes work.&lt;/p&gt;

&lt;p&gt;And then there was that last sentence: &lt;em&gt;"Should be easy, right?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my experience, "should be easy" is the most dangerous phrase in software. It is right up there with "it works on my machine" and "I'll just push a tiny fix on Friday."&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick background: what we built
&lt;/h2&gt;

&lt;p&gt;Our product is a customer support platform. Agents answer tickets. Customers send messages. Everyone is slightly angry about something.&lt;/p&gt;

&lt;p&gt;We built it with &lt;strong&gt;event sourcing&lt;/strong&gt;. If you don't know the term, here is the simple version.&lt;/p&gt;

&lt;p&gt;Think about your bank statement. The bank never goes back and erases a line. If you spend 10 euros by mistake, the bank adds a &lt;em&gt;new&lt;/em&gt; line that says "refund +10". The history stays the same. The current balance is just the sum of all lines.&lt;/p&gt;

&lt;p&gt;We did the same thing with tickets. Every change is an event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TicketOpened
MessageSent
AgentReplied
TicketReassigned
TicketClosed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We write events. We never update them. We never delete them. The state of any ticket is what you get when you replay its events from the start.&lt;/p&gt;

&lt;p&gt;This gave us a perfect audit trail, easy debugging, and the ability to rebuild any view of the data. We loved it. We gave talks about it. We had stickers.&lt;/p&gt;

&lt;p&gt;We now had &lt;strong&gt;420 million events&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And one person who wanted all of hers gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first instinct (a.k.a. the part where I almost made it worse)
&lt;/h2&gt;

&lt;p&gt;My brain said:&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;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'her-id'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line. Done by lunch. Hero of the company.&lt;/p&gt;

&lt;p&gt;Then my brain, which is smarter than my first brain, asked a few annoying questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Is her data only in the &lt;code&gt;user_id&lt;/code&gt; column?&lt;/strong&gt; No. People write things like &lt;em&gt;"Hi, my name is Anna, my phone is 555-0123, and my router is on fire."&lt;/em&gt; That is personal data inside free text. No column name will save you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Are her events alone?&lt;/strong&gt; No. They live in the same streams as agent replies, other customers who were added to the thread, and internal notes. Delete a whole stream and you delete other people's data too. That is a different problem, and a worse one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. What happens to the order of events?&lt;/strong&gt; They depend on each other. If you remove a &lt;code&gt;MessageSent&lt;/code&gt; event, then a later event that says &lt;em&gt;"agent replied to message #7"&lt;/em&gt; now points to nothing. Replays can crash. Totals stop adding up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Where else did her data go?&lt;/strong&gt; This was the scary one. Event sourcing means data is &lt;em&gt;copied&lt;/em&gt; by design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read models (the Postgres tables that power the UI)&lt;/li&gt;
&lt;li&gt;The search index&lt;/li&gt;
&lt;li&gt;The analytics warehouse&lt;/li&gt;
&lt;li&gt;The email provider that sent the notifications&lt;/li&gt;
&lt;li&gt;Application logs&lt;/li&gt;
&lt;li&gt;Backups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So even a perfect delete in the event store would leave her data in five or six other places. Like cleaning one room of a house that has six rooms, and all of them have glitter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pause. Your turn.
&lt;/h2&gt;

&lt;p&gt;Before you read the rest, try this. Take one minute and write down the &lt;strong&gt;questions you would ask before touching any code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'll wait. I have nothing else to do. I have 10 days.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Done? Good. Here's what I wrote down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The questions that mattered (none of them were about code)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is the request real?&lt;/strong&gt; Did we verify that the person asking is the person in the data? A fake erasure request is a nice way to destroy someone else's account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What does "everything" mean?&lt;/strong&gt; All tickets? Billing records? Just personal details?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do we have a legal reason to keep some of it?&lt;/strong&gt; GDPR Article 17 has exceptions. If we must keep invoices for tax law, or the data is needed for a legal claim, we may keep &lt;em&gt;that part&lt;/em&gt;. Not everything, but not nothing either. This is a question for Legal, not for me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whose data is mixed in?&lt;/strong&gt; Agents' names, other customers in the same thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can we ask for more time?&lt;/strong&gt; GDPR lets you extend the deadline for complex requests, but you must tell the person &lt;em&gt;within the first month&lt;/em&gt; and explain why. We had 10 days left to send that message at the latest. (Note to self: never look at this shared inbox on a Tuesday again.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice something? Four of five questions are not technical. This is what the exercise really tests. The code is the easy half.&lt;/p&gt;

&lt;h2&gt;
  
  
  The options on the table
&lt;/h2&gt;

&lt;p&gt;We had four ways to handle the event store itself.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;The catch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hard delete&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remove her events from the store&lt;/td&gt;
&lt;td&gt;Breaks the history. Breaks replays. Breaks trust.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redact in place&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replace the personal parts with &lt;code&gt;[erased]&lt;/code&gt;, keep the event shape&lt;/td&gt;
&lt;td&gt;You are &lt;em&gt;editing&lt;/em&gt; an append-only log. Needs a careful, audited migration.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Crypto-shredding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encrypt each person's data with their own key. To erase, destroy the key. The data is still there, but it is unreadable noise&lt;/td&gt;
&lt;td&gt;Beautiful idea. But you must do it &lt;em&gt;before&lt;/em&gt; writing the events.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Wait it out&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Let backups expire by their normal schedule&lt;/td&gt;
&lt;td&gt;Fine for backups. Not fine as the only plan.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Crypto-shredding is the answer everyone mentions first. And honestly, it's the right design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But we didn't do it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Crypto-shredding is like a seatbelt. A great idea. It just needs to be fitted &lt;em&gt;before&lt;/em&gt; the crash. Our 420 million events were already written in plain text. There was no key to destroy.&lt;/p&gt;

&lt;p&gt;So for &lt;em&gt;this&lt;/em&gt; request, we had to take the less glamorous path.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually did
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Find her data (the hard part).&lt;/strong&gt; We searched the event store, not just by user ID, but by her email, her phone number, and her name inside message text. We ran it as a &lt;strong&gt;dry run first&lt;/strong&gt;: count only, change nothing. The number of hits was higher than we expected. It always is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Redact, don't delete.&lt;/strong&gt; We wrote a controlled migration. It replaced the personal fields with a placeholder and kept the event types, order, and IDs. Every step was logged: who ran it, when, and on which events. The log itself did &lt;em&gt;not&lt;/em&gt; contain her data. (Yes, we checked. Twice. That would have been a very sad joke.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Fix the copies.&lt;/strong&gt; We rebuilt the read models from the redacted events. We removed her from the search index and the analytics warehouse. We sent a deletion request to the email provider. We checked the logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Handle the backups honestly.&lt;/strong&gt; We could not reach into old backups and edit them. So we did two things. We wrote down her erasure in a &lt;strong&gt;deletion ledger&lt;/strong&gt;. And we made a rule: if anyone ever restores from a backup, the ledger is replayed &lt;em&gt;before&lt;/em&gt; the system goes live again. The old backups age out on their normal schedule, and we told her that clearly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Answer the human.&lt;/strong&gt; We wrote back. We said what we erased, what we kept (and the legal reason), and when the last backup copy will disappear. Plain words. No hiding behind legal language.&lt;/p&gt;

&lt;p&gt;And for the future: new events now get &lt;strong&gt;per-person encryption keys&lt;/strong&gt;. The seatbelt is installed. We checked that it clicks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real lesson
&lt;/h2&gt;

&lt;p&gt;The word &lt;strong&gt;"deleted"&lt;/strong&gt; means three different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To an engineer: &lt;em&gt;the row is gone.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;To a lawyer: &lt;em&gt;not reasonably recoverable, and we can prove what we did.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;To the customer: &lt;em&gt;nobody has my data anymore.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The job is to make those three meanings meet. And you cannot do that with one SQL line. You do it by asking boring, annoying, slightly scary questions &lt;em&gt;first&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is why "should be easy, right?" is dangerous. It treats a &lt;strong&gt;design decision&lt;/strong&gt; like a &lt;strong&gt;task&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And one more thing. If you build any append-only system, ask this on day one, not on day 20:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"What will we do when someone asks us to forget?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because one day, someone will. And it will probably be a Tuesday.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm an engineer, not a lawyer, and this is not legal advice. GDPR details depend on your situation, so talk to someone qualified.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;This story is based on an exercise from &lt;a href="https://github.com/MohammadiSina/the-senior-leap" rel="noopener noreferrer"&gt;The Senior Leap&lt;/a&gt;, an open-source repo of hands-on system design scenarios. Each one asks you to think like a senior engineer &lt;em&gt;before&lt;/em&gt; you see the answer. It's called &lt;code&gt;erasure-request&lt;/code&gt; in the &lt;code&gt;system-design&lt;/code&gt; folder. Try it first, write your own analysis, and then compare.&lt;/p&gt;

&lt;p&gt;How would you have handled it? Tell me in the comments. Bonus points if you found a better trick for the backups.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>architecture</category>
      <category>backend</category>
      <category>gdpr</category>
    </item>
    <item>
      <title>Check this article. I found it essential.</title>
      <dc:creator>Sina Mohammadi</dc:creator>
      <pubDate>Wed, 30 Sep 2026 16:57:57 +0000</pubDate>
      <link>https://dev.to/mohammadi/check-this-article-i-found-it-essential-e9e</link>
      <guid>https://dev.to/mohammadi/check-this-article-i-found-it-essential-e9e</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/nestjs/nestjs-production-checklist-17-checks-before-you-deploy-1d0j" class="crayons-story__hidden-navigation-link"&gt;NestJS Production Checklist: 17 Checks Before You Deploy&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/nestjs/nestjs-production-checklist-17-checks-before-you-deploy-1d0j" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Includes strict Zod schema validation tips&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/nestjs"&gt;
            &lt;img alt="NestJS logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F940%2F9c4d32ad-272a-4c15-8a12-83ef48783d86.png" class="crayons-logo__image" width="400" height="400"&gt;
          &lt;/a&gt;

          &lt;a href="/kamilmysliwiec" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F196704%2F637eb949-df9c-4cb5-8b02-7a890cd56ce8.png" alt="kamilmysliwiec profile" class="crayons-avatar__image" width="500" height="500"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/kamilmysliwiec" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Kamil Mysliwiec
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Kamil Mysliwiec
                
                
              
              &lt;div id="story-author-preview-content-4776406" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/kamilmysliwiec" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F196704%2F637eb949-df9c-4cb5-8b02-7a890cd56ce8.png" class="crayons-avatar__image" alt="" width="500" height="500"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Kamil Mysliwiec&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/nestjs" class="crayons-story__secondary fw-medium"&gt;NestJS&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/nestjs/nestjs-production-checklist-17-checks-before-you-deploy-1d0j" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 30&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/nestjs/nestjs-production-checklist-17-checks-before-you-deploy-1d0j" id="article-link-4776406"&gt;
          NestJS Production Checklist: 17 Checks Before You Deploy
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nestjs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nestjs&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/node"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;node&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/nestjs/nestjs-production-checklist-17-checks-before-you-deploy-1d0j" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;22&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/nestjs/nestjs-production-checklist-17-checks-before-you-deploy-1d0j#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              10&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            17 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>backend</category>
      <category>deployment</category>
      <category>nestjs</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>The Ladder to Senior Engineer Is Broken, But You Can Climb It Anyway.</title>
      <dc:creator>Sina Mohammadi</dc:creator>
      <pubDate>Sat, 13 Jun 2026 10:36:44 +0000</pubDate>
      <link>https://dev.to/mohammadi/nobody-teaches-you-how-to-think-like-a-senior-engineer-1kk6</link>
      <guid>https://dev.to/mohammadi/nobody-teaches-you-how-to-think-like-a-senior-engineer-1kk6</guid>
      <description>&lt;p&gt;We are deep in the AI era, and AI is stunning at writing code. It refactors functions in seconds, generates entire boilerplate scaffolds, and sails through technical interviews. But AI doesn’t make the hard calls. It won’t tell you whether to add a message queue or just bump the thread pool when traffic spikes. It can’t feel the weight of a shortcut that will hold for three months and then trigger a cascading outage at 3 AM.&lt;/p&gt;

&lt;p&gt;Those decisions demand experience — the kind of instinct that only comes from watching real systems break in production, feeling the blast radius, and absorbing lessons that no textbook documents.&lt;/p&gt;

&lt;p&gt;This is where the industry has painted itself into a corner. Companies now need senior engineers more desperately than ever to steer AI tools and make architectural calls. Yet those same companies have largely stopped hiring juniors and mid-levels — the very people you mold into tomorrow’s seniors. The ladder that used to carry you from writing your first feature to designing resilient systems has been kicked away. Mid-level developers are stuck prompting AI and reviewing generated code, starved of the deep, messy, high-stakes problem-solving that builds true senior-level judgment.&lt;/p&gt;

&lt;p&gt;This gap between mid-level and senior is exactly why I built &lt;a href="https://github.com/MohammadiSina/the-senior-leap" rel="noopener noreferrer"&gt;&lt;strong&gt;The Senior Leap&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Most learning resources hand you sanitized, textbook exercises: “Design Twitter.” “Implement a rate limiter.” They have clean boundaries and perfect answers. Real systems are nothing like that. The exercises in The Senior Leap are built entirely on real-world war stories — actual production incidents, ugly architectural trade-offs, and the weird gotchas that only reveal themselves when you’re paged after midnight.&lt;/p&gt;

&lt;p&gt;Instead of asking you to write more code, the project drops you into a broken or incomplete system and demands an analysis. You lay out your reasoning, flag what you’re uncertain about, and commit to your read of the situation. Then you open the rubric. This isn’t a checklist of correct answers; it’s written from the perspective of a senior engineer who points out the exact failure mode you missed and the reframing question that would have changed your whole approach. It teaches you how to think, not just what to conclude.&lt;/p&gt;

&lt;p&gt;Finally, an AI evaluator (running locally or via providers like Anthropic or OpenRouter) reads your analysis against that rubric and gives you direct, pointed feedback: what you caught, what you missed, and why it would have mattered in a real incident. It simulates the very feedback loop that usually takes years of on-the-job scars to accumulate.&lt;/p&gt;

&lt;p&gt;If you’re a mid-level developer feeling stuck or worrying that AI is making you obsolete, hear this clearly: you are not obsolete. The industry still desperately needs people who can think critically, anticipate failures, and make tough calls when the playbook is empty. AI can write the code, but it needs a pilot. You can be that pilot — and this project is built to get you there.&lt;/p&gt;

&lt;p&gt;The jump from mid-level to senior has never been about memorizing more syntax or chasing the newest framework. It’s about a shift in how you see systems. That shift is a skill, not a secret handshake, and you can build it deliberately. You just need the right scenarios to train on, the ones that feel uncomfortably close to reality.&lt;/p&gt;

&lt;p&gt;The project is growing, and the scenarios are being built from real production stories. If you want to bridge the gap and start thinking like the senior engineer you’re meant to be, the door is open.&lt;/p&gt;

&lt;p&gt;And if you’re a senior engineer carrying hard-learned lessons from incidents and architectural crossroads, I invite you to turn those experiences into exercises. Help mid-level devs learn how you think — and in doing so, help rebuild the ladder we all still need.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>career</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
