<?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: Ahmed Abdelgawad</title>
    <description>The latest articles on DEV Community by Ahmed Abdelgawad (@ahmed_abdelgawad).</description>
    <link>https://dev.to/ahmed_abdelgawad</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%2F4006495%2Fabc973e3-f1a7-4be8-beb5-86364442d213.jpg</url>
      <title>DEV Community: Ahmed Abdelgawad</title>
      <link>https://dev.to/ahmed_abdelgawad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahmed_abdelgawad"/>
    <language>en</language>
    <item>
      <title>The Biggest Red Flag I Ever Heard in a Technical Interview</title>
      <dc:creator>Ahmed Abdelgawad</dc:creator>
      <pubDate>Fri, 17 Jul 2026 16:39:56 +0000</pubDate>
      <link>https://dev.to/ahmed_abdelgawad/the-biggest-red-flag-i-ever-heard-in-a-technical-interview-5513</link>
      <guid>https://dev.to/ahmed_abdelgawad/the-biggest-red-flag-i-ever-heard-in-a-technical-interview-5513</guid>
      <description>&lt;p&gt;A few years ago, I interviewed with a CTO&lt;/p&gt;

&lt;p&gt;Everything was going well until we started talking about automated testing.&lt;/p&gt;

&lt;p&gt;I explained how useful tests had been in some of the systems I worked on, especially when we had to change existing behaviour without breaking something completely unrelated.&lt;/p&gt;

&lt;p&gt;Then he interrupted me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"We don’t write tests because our business logic changes too quickly."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For a moment, I understood what he meant.&lt;/p&gt;

&lt;p&gt;They were a startup. Things changed all the time. Requirements moved fast, features were rewritten, and nothing stayed stable for very long.&lt;/p&gt;

&lt;p&gt;So why spend time testing something that might change again next week?&lt;/p&gt;

&lt;p&gt;But the more I thought about it, the stranger the statement became.&lt;/p&gt;

&lt;p&gt;He was using change as the reason not to write tests, when change was exactly the reason tests mattered.&lt;/p&gt;

&lt;p&gt;If your software barely changes, you might survive for a while with weak test coverage and a lot of manual checking.&lt;/p&gt;

&lt;p&gt;But if your business logic changes every week, you are constantly touching code that is already running in production.&lt;/p&gt;

&lt;p&gt;That is when the risk starts to add up.&lt;/p&gt;

&lt;p&gt;Fast-changing software does not need fewer tests.&lt;/p&gt;

&lt;p&gt;It needs a safer way to evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests Give You Something to Rely On
&lt;/h2&gt;

&lt;p&gt;Tests are usually described as a way to catch bugs.&lt;/p&gt;

&lt;p&gt;That is true, but it is not the main reason I value them.&lt;/p&gt;

&lt;p&gt;The bigger benefit is that they give you something to rely on when you change existing code.&lt;/p&gt;

&lt;p&gt;Imagine you are asked to update a pricing rule that has been in production for more than a year.&lt;/p&gt;

&lt;p&gt;The request sounds simple. A discount should no longer apply to a specific customer group.&lt;/p&gt;

&lt;p&gt;Writing the new condition is easy.&lt;/p&gt;

&lt;p&gt;The real problem is figuring out where else the current behaviour is used.&lt;/p&gt;

&lt;p&gt;Maybe the same rule exists in another checkout flow.&lt;/p&gt;

&lt;p&gt;Maybe one market has its own override that was added two years ago and never properly documented.&lt;/p&gt;

&lt;p&gt;Maybe an old campaign still depends on the current calculation.&lt;/p&gt;

&lt;p&gt;Without tests, you can read the code, check a few scenarios manually, and ask someone from product to verify the result.&lt;/p&gt;

&lt;p&gt;That may be enough in a small system.&lt;/p&gt;

&lt;p&gt;After a few years, it usually is not.&lt;/p&gt;

&lt;p&gt;Too much context ends up spread across code, tickets, old decisions, and people who may no longer be on the team. At that point, manual testing becomes less about verification and more about hoping everyone remembered the right things.&lt;/p&gt;

&lt;p&gt;Useful tests do not prove that the change is correct.&lt;/p&gt;

&lt;p&gt;They do not tell you that nothing can go wrong.&lt;/p&gt;

&lt;p&gt;They give you feedback about the behaviour the team already decided was important.&lt;/p&gt;

&lt;p&gt;And in a system that keeps changing, that feedback matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change Is the Normal State of Software
&lt;/h2&gt;

&lt;p&gt;Software is never really finished.&lt;/p&gt;

&lt;p&gt;Business rules change. Customers use features in ways nobody expected. Performance becomes a problem. Regulations evolve. Decisions that made sense two years ago stop making sense now.&lt;/p&gt;

&lt;p&gt;Sometimes the request is genuinely small.&lt;/p&gt;

&lt;p&gt;Other times, one small request exposes a design problem that has been sitting there for years.&lt;/p&gt;

&lt;p&gt;You add one condition and discover that the same rule is duplicated in four places.&lt;/p&gt;

&lt;p&gt;You try to update one workflow and realize that three unrelated modules depend on it.&lt;/p&gt;

&lt;p&gt;You fix one edge case and suddenly the change touches far more of the system than it should.&lt;/p&gt;

&lt;p&gt;This is where the lack of tests becomes expensive.&lt;/p&gt;

&lt;p&gt;Teams compensate with manual checks.&lt;/p&gt;

&lt;p&gt;The developer verifies the feature. Someone from product checks the happy path. Maybe a few related screens are tested before the release.&lt;/p&gt;

&lt;p&gt;That works until one of the forgotten cases breaks.&lt;/p&gt;

&lt;p&gt;Then releases become slower. More people get involved. Everyone becomes a little more careful around the same parts of the codebase.&lt;/p&gt;

&lt;p&gt;Eventually, some areas become known as dangerous.&lt;/p&gt;

&lt;p&gt;Everyone agrees they should be cleaned up, but nobody wants to be the person who breaks them.&lt;/p&gt;

&lt;p&gt;The frustrating part is that the engineers may know exactly what is wrong with the design.&lt;/p&gt;

&lt;p&gt;They may even know how to improve it.&lt;/p&gt;

&lt;p&gt;They just do not trust what will happen when they touch it.&lt;/p&gt;

&lt;p&gt;That is how temporary technical debt slowly becomes the architecture.&lt;/p&gt;

&lt;p&gt;Not because nobody noticed it.&lt;/p&gt;

&lt;p&gt;Because changing it feels riskier than leaving it alone.&lt;/p&gt;

&lt;p&gt;A useful test suite changes that.&lt;/p&gt;

&lt;p&gt;It gives the team enough feedback to improve the system gradually instead of waiting for one huge rewrite that may never happen.&lt;/p&gt;

&lt;p&gt;This does not mean every test suite is useful.&lt;/p&gt;

&lt;p&gt;A slow or flaky suite can create the same uncertainty it was supposed to remove. Tests can also become so tied to the implementation that harmless refactoring breaks them everywhere.&lt;/p&gt;

&lt;p&gt;The point is not to write as many tests as possible.&lt;/p&gt;

&lt;p&gt;The tests should protect the behaviour the business depends on without breaking every time the internal structure changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Sentence I Still Remember
&lt;/h2&gt;

&lt;p&gt;I do not remember anything about that interview anymore.&lt;/p&gt;

&lt;p&gt;I do not remember the architecture they described or any of the technical questions they asked.&lt;/p&gt;

&lt;p&gt;But I still remember that sentence.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“We don’t write tests because our business logic changes too quickly.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What made it such a red flag was not simply that they did not write tests.&lt;/p&gt;

&lt;p&gt;It was the reasoning behind it.&lt;/p&gt;

&lt;p&gt;They knew the software changed constantly, but instead of giving engineers a safer way to make those changes, they accepted uncertainty as part of the job.&lt;/p&gt;

&lt;p&gt;That told me something about the engineering culture.&lt;/p&gt;

&lt;p&gt;Engineers were expected to move quickly, but without the feedback needed to know whether their changes were safe. And when that becomes normal, people eventually stop improving the system. They avoid refactoring, work around bad design, and become more careful every time they touch old code.&lt;/p&gt;

&lt;p&gt;Since then, I have worked on systems where a tiny change took far longer to verify than to implement because nobody knew what else might break.&lt;/p&gt;

&lt;p&gt;I have also worked on systems where much larger changes were possible because the team had enough feedback to move step by step.&lt;/p&gt;

&lt;p&gt;That is why the sentence stayed with me.&lt;/p&gt;

&lt;p&gt;We do not write tests because the software is stable.&lt;/p&gt;

&lt;p&gt;We write them because engineers need the confidence to change it.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>testing</category>
      <category>refactorit</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Complexity Should Be Earned</title>
      <dc:creator>Ahmed Abdelgawad</dc:creator>
      <pubDate>Thu, 02 Jul 2026 06:00:00 +0000</pubDate>
      <link>https://dev.to/ahmed_abdelgawad/complexity-should-be-earned-nnf</link>
      <guid>https://dev.to/ahmed_abdelgawad/complexity-should-be-earned-nnf</guid>
      <description>&lt;p&gt;Every engineer eventually reaches a point where they stop asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How do I implement this?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and start asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What's the right architecture?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a sign of growth.&lt;/p&gt;

&lt;p&gt;It's usually when you discover concepts like Domain-Driven Design, CQRS, Event Sourcing, Hexagonal Architecture, and countless other patterns that promise cleaner, more maintainable systems.&lt;/p&gt;

&lt;p&gt;The mistake isn't learning those patterns.&lt;/p&gt;

&lt;p&gt;The mistake is looking for reasons to use them.&lt;/p&gt;

&lt;p&gt;Over the years, I've noticed something interesting: the more experienced engineers become, the more comfortable they are introducing complexity. Sometimes that's exactly what a system needs. But sometimes we're solving tomorrow's problems while today's business is still asking for a simple application.&lt;/p&gt;

&lt;p&gt;One of the most valuable engineering skills isn't knowing every architecture pattern.&lt;/p&gt;

&lt;p&gt;It's knowing &lt;strong&gt;when not to use one.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Every decision has a price
&lt;/h2&gt;

&lt;p&gt;Architecture discussions often focus on what patterns enable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CQRS separates reads from writes.&lt;/li&gt;
&lt;li&gt;Event Sourcing preserves the history of every change.&lt;/li&gt;
&lt;li&gt;Microservices allow teams to deploy independently.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are all legitimate advantages.&lt;/p&gt;

&lt;p&gt;But every architectural decision also introduces long-term costs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More concepts for new engineers to understand.&lt;/li&gt;
&lt;li&gt;More code paths to debug.&lt;/li&gt;
&lt;li&gt;More operational overhead.&lt;/li&gt;
&lt;li&gt;More infrastructure to maintain.&lt;/li&gt;
&lt;li&gt;More failure modes to think about.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those costs are worth paying &lt;strong&gt;only when they're solving a real problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the business isn't getting enough value in return, you've probably introduced complexity too early.&lt;/p&gt;

&lt;h2&gt;
  
  
  CRUD isn't something to "graduate" from
&lt;/h2&gt;

&lt;p&gt;CRUD isn't an architecture.&lt;/p&gt;

&lt;p&gt;It isn't a design pattern either.&lt;/p&gt;

&lt;p&gt;It's simply a way of describing applications whose primary behavior revolves around creating, reading, updating, and deleting data.&lt;/p&gt;

&lt;p&gt;Yet I often see teams treating CRUD as something temporary—as if every successful system eventually needs CQRS, Event Sourcing, or a fleet of microservices.&lt;/p&gt;

&lt;p&gt;I don't think that's the right mindset.&lt;/p&gt;

&lt;p&gt;A well-designed CRUD application can have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clear boundaries&lt;/li&gt;
&lt;li&gt;rich domain logic&lt;/li&gt;
&lt;li&gt;comprehensive tests&lt;/li&gt;
&lt;li&gt;clean abstractions&lt;/li&gt;
&lt;li&gt;excellent maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those qualities require advanced architectural patterns.&lt;/p&gt;

&lt;p&gt;Sometimes CRUD isn't the beginner solution.&lt;/p&gt;

&lt;p&gt;Sometimes it's simply the right solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let the business earn the complexity
&lt;/h2&gt;

&lt;p&gt;Imagine you're building an inventory system.&lt;/p&gt;

&lt;p&gt;At the beginning, inventory is just a quantity.&lt;/p&gt;

&lt;p&gt;Receive stock.&lt;/p&gt;

&lt;p&gt;Ship stock.&lt;/p&gt;

&lt;p&gt;Update the current value.&lt;/p&gt;

&lt;p&gt;A straightforward CRUD application is probably all you need.&lt;/p&gt;

&lt;p&gt;Now fast-forward two years.&lt;/p&gt;

&lt;p&gt;The business introduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple warehouses&lt;/li&gt;
&lt;li&gt;stock reservations&lt;/li&gt;
&lt;li&gt;returns&lt;/li&gt;
&lt;li&gt;warehouse transfers&lt;/li&gt;
&lt;li&gt;third-party fulfillment&lt;/li&gt;
&lt;li&gt;inventory audits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suddenly, inventory is no longer just a number.&lt;/p&gt;

&lt;p&gt;Different parts of the system care about different views of the same data. Historical changes become valuable. Read and write workloads start evolving independently.&lt;/p&gt;

&lt;p&gt;At that point, introducing concepts like projections or CQRS might actually make the system &lt;strong&gt;simpler&lt;/strong&gt;, not more complicated.&lt;/p&gt;

&lt;p&gt;Notice what happened.&lt;/p&gt;

&lt;p&gt;The architecture changed because the business changed.&lt;/p&gt;

&lt;p&gt;Not because someone wanted to use a particular pattern.&lt;/p&gt;

&lt;p&gt;That's exactly how I believe software should evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the problem, not the pattern
&lt;/h2&gt;

&lt;p&gt;One of the most common architecture questions I hear is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Should we use this or that pattern?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think there's usually a better question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What problem are we trying to solve?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes the answer genuinely leads to CQRS.&lt;/p&gt;

&lt;p&gt;Sometimes it leads to Event Sourcing.&lt;/p&gt;

&lt;p&gt;Sometimes it leads to a modular monolith.&lt;/p&gt;

&lt;p&gt;And sometimes it leads to a straightforward CRUD application.&lt;/p&gt;

&lt;p&gt;The pattern isn't the goal.&lt;/p&gt;

&lt;p&gt;It's the consequence of understanding the problem.&lt;/p&gt;

&lt;p&gt;Good architecture isn't about collecting patterns.&lt;/p&gt;

&lt;p&gt;It's about making thoughtful trade-offs.&lt;/p&gt;

&lt;p&gt;The hardest architectural decision isn't choosing the most sophisticated solution.&lt;/p&gt;

&lt;p&gt;It's recognizing when the simplest one is enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good engineers learn patterns.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Great engineers learn when not to use them.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
