<?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: Ayman Atif</title>
    <description>The latest articles on DEV Community by Ayman Atif (@a95yman).</description>
    <link>https://dev.to/a95yman</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%2F3887349%2F7c6fd8fc-d57f-403c-baae-674bdf090ac8.jpg</url>
      <title>DEV Community: Ayman Atif</title>
      <link>https://dev.to/a95yman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/a95yman"/>
    <language>en</language>
    <item>
      <title>The Screen Looked Finished. Then I Clicked Save Twice.</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Fri, 06 Nov 2026 15:15:03 +0000</pubDate>
      <link>https://dev.to/a95yman/the-screen-looked-finished-then-i-clicked-save-twice-9l3</link>
      <guid>https://dev.to/a95yman/the-screen-looked-finished-then-i-clicked-save-twice-9l3</guid>
      <description>&lt;p&gt;&lt;em&gt;AI coding agents write code faster than ever. Here's the six-step workflow I use to turn that speed into software that actually holds up.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ask an AI coding agent for a product-management page and you'll have one in minutes. A clean form, a couple of buttons, a tidy list of products. It opens in the browser. It looks done.&lt;/p&gt;

&lt;p&gt;Now ask it a few ordinary questions.&lt;/p&gt;

&lt;p&gt;Does the form reject a negative price? Is the product still there after the server restarts? Can a team member edit something they aren't allowed to manage? And what happens when someone clicks Save twice?&lt;/p&gt;

&lt;p&gt;Those questions are the whole gap between &lt;em&gt;generating code&lt;/em&gt; and &lt;em&gt;building software&lt;/em&gt;. The screen is one visible part. The rest is data that stays saved, rules that hold, access that's enforced, and some way to check that all of it behaves the way you planned.&lt;/p&gt;

&lt;p&gt;Here's the good part: closing that gap is a learnable process. And once you have it, AI agents become one of the most enjoyable ways to build that I've found in ten years of doing this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I learned this
&lt;/h2&gt;

&lt;p&gt;I've been building software professionally since 2016, most recently as a freelance .NET and Python engineer.&lt;/p&gt;

&lt;p&gt;One project taught me more about this gap than any other. A client needed a business valuation and deal platform: valuation analysis, document drafting, confidential memorandums, controlled sharing, PDF and spreadsheet imports, and print-ready exports. I built it from scratch, from planning and architecture through deployment, and then maintained it for about two years.&lt;/p&gt;

&lt;p&gt;The hardest problems never showed up in a demo.&lt;/p&gt;

&lt;p&gt;The first was two people working on the same shared document at the same moment. Each save was fine on its own. Together, they could quietly overwrite each other. The second was keeping the app responsive on a modest server when many people used it at once.&lt;/p&gt;

&lt;p&gt;Neither was solved by writing code faster. Both were solved by deciding exactly what &lt;em&gt;should&lt;/em&gt; happen, writing it down, and then checking that it did. That's the habit I now bring to every AI-assisted build, and it's what makes the speed worth having.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build with a two-AI team
&lt;/h2&gt;

&lt;p&gt;The setup that works best for me gives each tool a clear job.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT is the planner.&lt;/strong&gt; It helps clarify the goal, turn an idea into requirements, compare options and write a focused task for the coding agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code is the builder.&lt;/strong&gt; It inspects the real repository, proposes a plan based on the actual code, implements approved changes and runs the checks you allow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You are the decision-maker.&lt;/strong&gt; You choose the goal, approve important decisions, review the evidence and decide when something is finished.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The link between them is a &lt;em&gt;handoff&lt;/em&gt;: a short, scoped message that asks the coding agent to look before it touches anything. Here's one you can copy today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Goal:
Allow an authorized business owner to create a digital product.

Before changing files:
1. Inspect the existing product-management flow.
2. Identify the relevant pages, business logic, data model, and tests.
3. Report the current behavior and propose a focused plan.
4. List any assumptions or decisions that need approval.

Do not edit files yet. Do not access production systems or data.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It asks for information and a plan, not code. That one pause lets you catch a misunderstanding while it's still a sentence, not a hundred changed lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  The six-step loop: Inspect, Plan, Approve, Implement, Verify, Document
&lt;/h2&gt;

&lt;p&gt;Every meaningful change goes through the same loop.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inspect.&lt;/strong&gt; The agent reads the relevant code and reports what's there today, separating what it observed from what it assumed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan.&lt;/strong&gt; You agree on what the change should do, what it should leave alone, and how you'll know it works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approve.&lt;/strong&gt; You read the plan and say yes, in writing, before any file changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement.&lt;/strong&gt; The agent makes the smallest change that satisfies the plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify.&lt;/strong&gt; You check the evidence: the actual diff, the test results, the behavior in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document.&lt;/strong&gt; You record the decision and save a Git checkpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a tiny cosmetic edit, some steps can be lighter. Anything touching customer data, permissions, payments or the database gets the full loop.&lt;/p&gt;

&lt;p&gt;The Plan step is where most of the value lives, and its core is a few acceptance criteria written &lt;em&gt;before&lt;/em&gt; the code. For that product page, they might be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An authorized business owner can submit a product name and a valid, non-negative price.&lt;/li&gt;
&lt;li&gt;Invalid information gets a clear message about which fields to fix.&lt;/li&gt;
&lt;li&gt;A successful save shows the new product in the list.&lt;/li&gt;
&lt;li&gt;The product is still there after a page refresh.&lt;/li&gt;
&lt;li&gt;A user without permission can't create a product, even by sending the request directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five plain sentences. They don't dictate a single line of code, yet they tell the agent exactly what done means and tell you exactly what to test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four habits that keep a project steady
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Build in small slices.&lt;/strong&gt; List products. Then create a product. Then edit one. Each slice gets its own plan, its own checks and its own Git commit. Small steps are easy to review and easy to undo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give the agent a short, true project memory.&lt;/strong&gt; A concise CLAUDE.md, a README, a note on how data is kept separate between users, and a record of key decisions. The agent reads them every session, so day thirty is as consistent as day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep production off limits to the agent.&lt;/strong&gt; It works locally, with synthetic test data and secrets stored outside the code. Payments run in test mode until you're ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check the evidence, not the summary.&lt;/strong&gt; An agent's "Done! All tests pass" is a claim. The diff, the test output and the page in your browser are the evidence. Read them, and ask "why this way?" when something surprises you. You'll learn a lot just from that question.&lt;/p&gt;

&lt;h2&gt;
  
  
  The full field guide
&lt;/h2&gt;

&lt;p&gt;Everything above is the opening of a much bigger workflow. I wrote it all down in &lt;strong&gt;&lt;a href="https://yaman95.gumroad.com/l/building-real-software-with-ai" rel="noopener noreferrer"&gt;Building Real Software With AI: A Practical Engineering Field Guide&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Across 40 chapters, you build one complete web application with AI coding agents: a platform where businesses sell digital products like guides, templates and recorded lessons. It starts with a small, working core and grows one verified feature at a time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan before you build:&lt;/strong&gt; requirements, scope for every change, choosing a stack, architecture and a project knowledge base&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up safely:&lt;/strong&gt; terminal basics, Git before the first line of code, Claude Code permissions, environments and secrets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the MVP:&lt;/strong&gt; your first app, database and features, plus testing and debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add real users and money:&lt;/strong&gt; accounts, separate business workspaces, roles, orders, Stripe payments in test mode, email and reports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it dependable:&lt;/strong&gt; security, privacy, performance, integrations, accessibility and release checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy and operate:&lt;/strong&gt; hosting, deployment, day-to-day operation and recovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the toolkit:&lt;/strong&gt; a prompt library, checklists, copy-ready templates, filled-in examples and a glossary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack is C#, ASP.NET Core, PostgreSQL, Entity Framework Core, xUnit, Playwright and GitHub Actions. Each chapter explains what the code does and why, so some programming experience in any language is enough to follow along.&lt;/p&gt;

&lt;p&gt;It's for developers who can already get code out of AI and want it to hold up, self-taught builders working on a first serious app, and freelancers who want a calmer, more disciplined workflow.&lt;/p&gt;

&lt;p&gt;It's $29.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://yaman95.gumroad.com/l/building-real-software-with-ai" rel="noopener noreferrer"&gt;Get Building Real Software With AI on Gumroad&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And if you'd like to start right now, pick one small feature in your current project and write five acceptance criteria for it before you open the agent. It's a small change that makes everything after it smoother.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I write about software engineering and building with AI here on Medium. You can see more of my work at &lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;ayman-atif.vercel.app&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
      <category>claude</category>
      <category>chatgpt</category>
    </item>
    <item>
      <title>The Test Your AI Rewrote to Go Green</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Tue, 03 Nov 2026 14:13:00 +0000</pubDate>
      <link>https://dev.to/a95yman/the-test-your-ai-rewrote-to-go-green-5gmk</link>
      <guid>https://dev.to/a95yman/the-test-your-ai-rewrote-to-go-green-5gmk</guid>
      <description>&lt;p&gt;A passing test suite feels good. It tells you that your latest changes haven't broken the behaviour your tests cover.&lt;/p&gt;

&lt;p&gt;But what if an AI coding agent makes the tests pass by changing what they're supposed to check?&lt;/p&gt;

&lt;p&gt;I'm Ayman Atif, a software developer based in Morocco. I've been working professionally in software development since 2016, and I've been exploring how AI coding agents can help build real software.&lt;/p&gt;

&lt;p&gt;One thing I've become particularly interested in is the difference between generating code and verifying that the code actually does what it's supposed to do.&lt;/p&gt;

&lt;p&gt;Here's an example that illustrates the distinction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The feature worked—or did it?
&lt;/h2&gt;

&lt;p&gt;Imagine a small application where business owners can manage their products.&lt;/p&gt;

&lt;p&gt;A user wants to rename a product. The new name must be unique within that business.&lt;/p&gt;

&lt;p&gt;If the user tries to rename a product to a name that already exists, the application should display a friendly validation message.&lt;/p&gt;

&lt;p&gt;That's a straightforward requirement.&lt;/p&gt;

&lt;p&gt;We can express it as a test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create two products with different names.&lt;/li&gt;
&lt;li&gt;Attempt to rename one using the other's name.&lt;/li&gt;
&lt;li&gt;Verify that the operation is rejected.&lt;/li&gt;
&lt;li&gt;Verify that the user receives a useful validation message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The test isn't merely checking whether the operation failed. It's checking &lt;em&gt;how&lt;/em&gt; the application handles the situation.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the proposed fix changes the test
&lt;/h2&gt;

&lt;p&gt;During work on an example application, a test exposed a problem with this behaviour.&lt;/p&gt;

&lt;p&gt;The expected result was a friendly validation message. Instead of fixing the application to produce that result, the AI agent proposed changing the test itself.&lt;/p&gt;

&lt;p&gt;The original expectation was replaced with something equivalent to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ThrowsAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DbUpdateException&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;RenameProductAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;existingName&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;Now the test expected a database exception.&lt;/p&gt;

&lt;p&gt;The test could pass, but the application still returned a server error instead of the validation response the user should receive.&lt;/p&gt;

&lt;p&gt;The requirement hadn't changed. Only the test had.&lt;/p&gt;

&lt;p&gt;This is an important distinction: a test can pass while the application still fails to meet its intended behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Green doesn't always mean correct
&lt;/h2&gt;

&lt;p&gt;AI coding agents are often asked to fix failing tests.&lt;/p&gt;

&lt;p&gt;That's a reasonable task, but the instruction needs context.&lt;/p&gt;

&lt;p&gt;A failing test might indicate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A genuine application bug.&lt;/li&gt;
&lt;li&gt;An incorrect test expectation.&lt;/li&gt;
&lt;li&gt;An incomplete requirement.&lt;/li&gt;
&lt;li&gt;A mismatch between the implementation and the intended behaviour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Changing the test is sometimes the right decision. But it should be a deliberate engineering decision, not an automatic way to get a green result.&lt;/p&gt;

&lt;p&gt;In this example, the test represented an existing requirement. Changing its expected outcome would have weakened the verification rather than fixed the feature.&lt;/p&gt;

&lt;p&gt;The appropriate response was to investigate the application behaviour and correct the implementation while preserving the original expectation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I take from this
&lt;/h2&gt;

&lt;p&gt;When working with an AI coding agent, I don't think the question should be limited to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did the tests pass?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd also ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Did the agent modify existing tests?&lt;/li&gt;
&lt;li&gt;If so, why?&lt;/li&gt;
&lt;li&gt;Does the new expectation still represent the original requirement?&lt;/li&gt;
&lt;li&gt;Was the application behaviour actually corrected?&lt;/li&gt;
&lt;li&gt;Can I explain why the test now passes?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These questions don't require a complicated testing framework. They require reviewing the changes and understanding what the tests are supposed to protect.&lt;/p&gt;

&lt;p&gt;A green test suite is useful evidence. But it isn't a substitute for engineering judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  One rule I try to follow
&lt;/h2&gt;

&lt;p&gt;When an AI agent proposes changing an existing test, I want to understand the reason before accepting that change.&lt;/p&gt;

&lt;p&gt;Sometimes the test is wrong. Sometimes the implementation is wrong. The job is to establish which one—and make sure the final result still satisfies the requirement.&lt;/p&gt;

&lt;p&gt;That's one of the principles behind my approach to building software with AI: let the agent help with implementation, but keep the responsibility for decisions and verification with the engineer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you ever reviewed an AI-generated fix and discovered that it changed the test instead of fixing the underlying behaviour?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>I'm a Software Engineer, and I'm Choosing to Build With AI. Here's Why.</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Sun, 01 Nov 2026 17:22:07 +0000</pubDate>
      <link>https://dev.to/a95yman/im-a-software-engineer-and-im-choosing-to-build-with-ai-heres-why-5dc2</link>
      <guid>https://dev.to/a95yman/im-a-software-engineer-and-im-choosing-to-build-with-ai-heres-why-5dc2</guid>
      <description>&lt;h1&gt;
  
  
  I'm a Software Engineer, and I'm Choosing to Build With AI. Here's Why.
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;AI is changing software development. I don't think developers should fight that change. I think we should learn to use it properly.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've been a software engineer for years. I've spent a good part of my career learning technologies, solving problems, debugging applications, and figuring out how to turn ideas into working software.&lt;/p&gt;

&lt;p&gt;And recently, I've been spending more and more time working with AI.&lt;/p&gt;

&lt;p&gt;Not just asking it questions or generating small code snippets. I mean using AI coding agents to work on actual software projects: exploring codebases, designing features, implementing changes, investigating bugs, and reviewing the results.&lt;/p&gt;

&lt;p&gt;It's changed the way I approach development.&lt;/p&gt;

&lt;p&gt;And I think we're only at the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  We need to stop treating AI as something developers must either love or hate
&lt;/h2&gt;

&lt;p&gt;There's a lot of debate around AI in software development.&lt;/p&gt;

&lt;p&gt;Some people see it as the future of programming. Others worry that developers will become less skilled, that AI-generated code is unreliable, or that we're slowly giving away the thinking that makes us engineers.&lt;/p&gt;

&lt;p&gt;I understand those concerns.&lt;/p&gt;

&lt;p&gt;But I don't think ignoring AI is the answer.&lt;/p&gt;

&lt;p&gt;Software development is changing. Whether we like every part of that change or not, AI coding tools are becoming increasingly capable. They can help developers move faster, explore unfamiliar technologies, and tackle work that might previously have required significantly more time.&lt;/p&gt;

&lt;p&gt;That doesn't mean we should blindly trust everything they produce.&lt;/p&gt;

&lt;p&gt;It means we need to become better at working with them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The goal shouldn't be to let AI replace engineering judgment. It should be to use AI while keeping engineering judgment firmly in the process.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing code is only part of building software
&lt;/h2&gt;

&lt;p&gt;One thing becomes obvious when you use AI to build something beyond a small demonstration: generating code is not the same as delivering software.&lt;/p&gt;

&lt;p&gt;An AI agent can implement a feature that looks correct but breaks something else.&lt;/p&gt;

&lt;p&gt;It can introduce a security weakness, misunderstand a business requirement, overlook an edge case, or make a change that works in isolation but fails when combined with the rest of the application.&lt;/p&gt;

&lt;p&gt;And sometimes, the result looks impressive enough that you don't immediately notice the problem.&lt;/p&gt;

&lt;p&gt;This is where the developer's role matters.&lt;/p&gt;

&lt;p&gt;You still need to understand what you're building. You need to inspect the existing system, define requirements, make architectural decisions, review changes, test behavior, and think about what happens when things go wrong.&lt;/p&gt;

&lt;p&gt;AI can help with these activities. It can even perform a significant amount of the implementation work.&lt;/p&gt;

&lt;p&gt;But someone needs to remain responsible for the result.&lt;/p&gt;

&lt;p&gt;That's the approach I want to explore.&lt;/p&gt;

&lt;h2&gt;
  
  
  I'm working on something called Building Real Software with AI
&lt;/h2&gt;

&lt;p&gt;I've started developing a project called &lt;strong&gt;Building Real Software with AI — A Practical Engineering Field Guide&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The idea is straightforward:&lt;/p&gt;

&lt;p&gt;I want to help developers move beyond generating isolated code snippets and learn how to use AI coding agents to build complete applications responsibly.&lt;/p&gt;

&lt;p&gt;Not another collection of clever prompts.&lt;/p&gt;

&lt;p&gt;Not a promise that you can build a production-ready business application in one weekend.&lt;/p&gt;

&lt;p&gt;And definitely not a guide that assumes AI-generated code is automatically correct.&lt;/p&gt;

&lt;p&gt;I want to focus on the actual engineering process.&lt;/p&gt;

&lt;p&gt;How do you approach an unfamiliar codebase? How do you turn an idea into clear requirements? How do you give an AI agent enough direction without surrendering control? How do you review its implementation? How do you test the result? And how do you think about security, payments, data integrity, deployment, and maintenance?&lt;/p&gt;

&lt;p&gt;These are the questions I want the project to address.&lt;/p&gt;

&lt;p&gt;The guide uses a continuous example: a fictional multi-business digital-products platform. Instead of jumping between unrelated toy examples, the reader follows a system through different stages of development.&lt;/p&gt;

&lt;p&gt;One principle guides the entire approach:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inspect → Plan → Approve → Implement → Verify → Document.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI should help you build. It shouldn't make you stop thinking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why start with a PDF?
&lt;/h2&gt;

&lt;p&gt;I'm deliberately starting with a PDF.&lt;/p&gt;

&lt;p&gt;I could spend months building a website, a learning platform, an interactive course, or a collection of tools around the idea.&lt;/p&gt;

&lt;p&gt;But I'd rather start with the actual learning material.&lt;/p&gt;

&lt;p&gt;A PDF is a practical first format. It gives me a way to organize the ideas, explain the workflow, include examples, and put something tangible in front of readers.&lt;/p&gt;

&lt;p&gt;I'm treating this as the first version of a larger project, not pretending that the entire product experience is already finished.&lt;/p&gt;

&lt;p&gt;There are other things I'm considering for later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A companion GitHub repository with a reference implementation.&lt;/li&gt;
&lt;li&gt;Practical exercises and templates developers can use in their own projects.&lt;/li&gt;
&lt;li&gt;Checklists for reviewing AI-generated code and implementation decisions.&lt;/li&gt;
&lt;li&gt;Additional resources and updates as the guide evolves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I haven't committed to all of these. I want to see what readers actually find useful before deciding how far to take the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who am I building this for?
&lt;/h2&gt;

&lt;p&gt;Primarily, developers who already understand at least some programming but want to become more effective with AI coding agents.&lt;/p&gt;

&lt;p&gt;Perhaps you're self-taught.&lt;/p&gt;

&lt;p&gt;Perhaps you're a junior developer trying to build more substantial projects.&lt;/p&gt;

&lt;p&gt;Perhaps you're a freelancer who wants to deliver better software for clients.&lt;/p&gt;

&lt;p&gt;Or perhaps you're an experienced developer who has started using AI tools but isn't entirely comfortable trusting the results.&lt;/p&gt;

&lt;p&gt;I don't think you need to become an AI expert to benefit from these tools.&lt;/p&gt;

&lt;p&gt;But I do think you need to understand software engineering well enough to recognize when an AI agent is making a bad decision.&lt;/p&gt;

&lt;p&gt;And if you don't know something, you should learn how to investigate it rather than simply accepting the generated answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  I'm not trying to predict the future of software development
&lt;/h2&gt;

&lt;p&gt;I don't know exactly what software engineering will look like five years from now.&lt;/p&gt;

&lt;p&gt;I don't know which AI tools will dominate, which workflows will become standard, or how much of today's development process will change.&lt;/p&gt;

&lt;p&gt;But I do know that developers who learn to work effectively with these tools will have opportunities to do more.&lt;/p&gt;

&lt;p&gt;I also believe that the fundamentals will remain important: understanding requirements, designing systems, reasoning about data, testing behavior, protecting users, and taking responsibility for what we ship.&lt;/p&gt;

&lt;p&gt;AI changes how we work. It doesn't make those responsibilities disappear.&lt;/p&gt;

&lt;p&gt;That's why I'm building this project.&lt;/p&gt;

&lt;p&gt;I want to explore what responsible AI-assisted software development actually looks like, document the process, and make the lessons accessible to other developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building Real Software with AI is still evolving. The PDF is the first step.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If this is something you're interested in, I'd love to hear from you.&lt;/p&gt;

&lt;p&gt;What has your experience with AI coding agents been like? Have you built something substantial with them? What do you wish someone had explained before you started?&lt;/p&gt;

&lt;p&gt;I'm particularly interested in the gap between getting AI to generate code and getting AI to help deliver software you can confidently maintain.&lt;/p&gt;

&lt;p&gt;That's the gap I want to work on.&lt;/p&gt;

&lt;p&gt;I’m Ayman Atif, a freelance software developer. I build business software for startups, small businesses and agencies, and a good share of that work starts with an old spreadsheet or database that nobody wants to touch.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Prompt Engineering Mistake That Turns a Small Feature Into a Big Refactor</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Sun, 25 Oct 2026 17:41:07 +0000</pubDate>
      <link>https://dev.to/a95yman/the-prompt-engineering-mistake-that-turns-a-small-feature-into-a-big-refactor-18j1</link>
      <guid>https://dev.to/a95yman/the-prompt-engineering-mistake-that-turns-a-small-feature-into-a-big-refactor-18j1</guid>
      <description>&lt;p&gt;&lt;em&gt;Why asking AI to code before it understands your codebase is a costly shortcut.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A feature request can fit in one sentence. The changes AI makes to implement it can affect half your application.&lt;/p&gt;

&lt;p&gt;The problem often starts with a simple prompt:&lt;/p&gt;

&lt;p&gt;“Add a notification system to this application.”&lt;/p&gt;

&lt;p&gt;The request sounds reasonable. The AI has a clear objective, and it can generate code quickly. But it doesn't necessarily know how your application handles permissions, where business rules belong, which components are shared, or what existing behavior must remain unchanged.&lt;/p&gt;

&lt;p&gt;So it makes assumptions.&lt;/p&gt;

&lt;p&gt;It might introduce a new service when an existing one already handles similar operations. It might duplicate business logic, modify a shared component, or add a dependency that the project doesn't need.&lt;/p&gt;

&lt;p&gt;The code can look clean, the feature can appear to work, and the implementation can still be wrong for the application.&lt;/p&gt;

&lt;p&gt;I'm Ayman Atif, a freelance software engineer working with business software, APIs, automation, and data processing. I share more of my work and engineering projects on &lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;my portfolio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One lesson from building software is that &lt;strong&gt;getting AI to produce code is not the same as getting it to make the right change.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake: treating implementation as the first step
&lt;/h2&gt;

&lt;p&gt;Prompt engineering is often discussed as a matter of finding the right words, adding more context, or making instructions more detailed.&lt;/p&gt;

&lt;p&gt;Those things can help. But there's a more fundamental issue: the order in which you ask AI to work.&lt;/p&gt;

&lt;p&gt;When you immediately ask for an implementation, you're encouraging the model to solve the problem before it has established how the problem fits into the existing system.&lt;/p&gt;

&lt;p&gt;A detailed prompt doesn't automatically solve this. You can write several paragraphs describing the desired feature and still leave important architectural questions unanswered.&lt;/p&gt;

&lt;p&gt;For example, consider this request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add email notifications when an order is completed. Make sure the code is clean and follow best practices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The instructions sound good, but they leave several questions open.&lt;/p&gt;

&lt;p&gt;Does the application already have an email service? Where is an order considered complete? Could the operation run more than once? What happens if sending the email fails? Which files should change, and which must remain untouched?&lt;/p&gt;

&lt;p&gt;The model may choose reasonable answers to these questions. The issue is that reasonable assumptions are not necessarily correct assumptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change the prompt, not just the wording
&lt;/h2&gt;

&lt;p&gt;Instead of asking AI to implement the feature immediately, split the work into two stages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage one: understand the system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Try this prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Inspect the existing codebase to understand how order completion works and how email notifications are currently handled.&lt;/p&gt;

&lt;p&gt;Do not modify any files yet.&lt;/p&gt;

&lt;p&gt;Identify the relevant components, existing services, business rules, and potential side effects. Explain the smallest change that would implement this feature while preserving existing behavior.&lt;/p&gt;

&lt;p&gt;If an important requirement is unclear, ask before proceeding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This changes the immediate objective. The AI must first build a picture of the existing implementation and identify the assumptions that could affect its solution.&lt;/p&gt;

&lt;p&gt;You can then review its findings, correct misunderstandings, and decide whether the proposed approach fits your requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage two: implement the agreed approach.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the inspection is satisfactory, give the AI a separate instruction:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Implement the agreed change using the existing architecture. Keep the scope limited to the files and behavior identified during planning. Avoid unnecessary dependencies and unrelated refactoring. Add or update tests for the new behavior and relevant regressions. Report the files changed, the tests run, and any remaining risks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The difference isn't that the second prompt contains magic words. It's that implementation now follows an explicit understanding of the codebase and an approach you've reviewed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters beyond prompt engineering
&lt;/h2&gt;

&lt;p&gt;This habit becomes particularly valuable in business applications, where a seemingly small change can affect permissions, financial calculations, background jobs, or data shared across multiple customers.&lt;/p&gt;

&lt;p&gt;A notification feature, for example, might appear harmless. But if the underlying event can fire twice, customers could receive duplicate messages. If the implementation bypasses an existing authorization check, the problem could be more serious than a duplicated notification.&lt;/p&gt;

&lt;p&gt;AI can help identify these risks, but it cannot be assumed to understand every constraint simply because the request sounds specific.&lt;/p&gt;

&lt;p&gt;The engineer still needs to decide what must remain true, verify the proposed changes, and test the behavior that matters.&lt;/p&gt;

&lt;p&gt;That is where good prompt engineering becomes part of good software engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical rule I use
&lt;/h2&gt;

&lt;p&gt;Before asking AI to change an existing application, ask it to explain the relevant code and propose a minimal plan first.&lt;/p&gt;

&lt;p&gt;Review that plan before allowing implementation to begin.&lt;/p&gt;

&lt;p&gt;This doesn't need to become a lengthy process for every change. A small, isolated task may require only a quick inspection. A change involving authentication, payments, tenant isolation, or shared infrastructure deserves more scrutiny.&lt;/p&gt;

&lt;p&gt;The point is to match the amount of investigation to the potential impact of the change.&lt;/p&gt;

&lt;p&gt;AI is useful precisely because it can help us move faster. Understanding the existing system first helps ensure that the speed produces a better result rather than more code to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best first prompt isn't always “Build this.” Sometimes it's “Understand what already exists, then tell me how you would change it.”&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Ayman Atif, a freelance software engineer focused on business software and backend development. Explore my projects and engineering work at &lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;ayman-atif.vercel.app&lt;/a&gt;.&lt;/em&gt; &lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Migrating a Microsoft Access Database to a Web App: What Moving an Agency’s CRM Taught Me</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:06:46 +0000</pubDate>
      <link>https://dev.to/a95yman/migrating-a-microsoft-access-database-to-a-web-app-what-moving-an-agencys-crm-taught-me-2dpp</link>
      <guid>https://dev.to/a95yman/migrating-a-microsoft-access-database-to-a-web-app-what-moving-an-agencys-crm-taught-me-2dpp</guid>
      <description>&lt;p&gt;&lt;em&gt;The import isn't the hard part. Keeping your records connected is.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I rebuilt a CRM for an agency three times. Not because the code kept falling over, but because I kept finding out what the job actually was.&lt;/p&gt;

&lt;p&gt;The agency hired me as an independent freelancer. Their contacts and deals lived in a Microsoft Access database, and all of it had to come across into a new CRM. On paper, that sounds like exporting a few tables and importing them somewhere else. It wasn't. The hardest part had nothing to do with screens or features. It was making sure every deal was still attached to the right person after the move.&lt;/p&gt;

&lt;p&gt;If your business runs on an Access database and you're thinking about moving it to a web app, here's what I'd want you to know before you start. Or before you hire someone to do it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why businesses end up here
&lt;/h2&gt;

&lt;p&gt;A lot of Access databases start the same way. Someone builds a few tables and forms, it works, and the business slowly grows around it. Years later it holds the contacts, the deals, the history, and a fair amount of knowledge that nobody ever wrote down anywhere else.&lt;/p&gt;

&lt;p&gt;For this agency, the reason was simple. They wanted their database hosted in the cloud instead of sitting in an Access file.&lt;/p&gt;

&lt;p&gt;None of that means Access was a mistake. It got the business this far. The trouble starts when you try to leave.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part everyone underestimates: relationships
&lt;/h2&gt;

&lt;p&gt;Here's the thing that's easy to miss until you hit it.&lt;/p&gt;

&lt;p&gt;In an Access database, every contact has an ID. Every deal points at a contact by that ID. Say a deal belongs to contact 1042. That number is how the database knows whose deal it is.&lt;/p&gt;

&lt;p&gt;When you move into a new system, the new system usually generates its own IDs. Contact 1042 in Access might become contact 87 in the new CRM. That's normal. But if you import the contacts, then import the deals on their own, the deals still say "1042". In the new system, 1042 is either somebody else entirely or nobody at all.&lt;/p&gt;

&lt;p&gt;The scary part is that nothing looks broken. You end up with valid-looking contacts and valid-looking deals, and the connections between them are gone. Or worse, quietly wrong.&lt;/p&gt;

&lt;p&gt;So the critical piece of the whole migration wasn't the importer. It was a mapping: for every record in Access, which new ID did it become? Once that mapping exists, every deal can be translated to point at the right contact in the new system.&lt;/p&gt;

&lt;p&gt;It's boring bookkeeping. It's also the whole job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your data is messier than you think
&lt;/h2&gt;

&lt;p&gt;That mapping would be easy if every contact were cleanly identifiable. They weren't.&lt;/p&gt;

&lt;p&gt;The agency's data had duplicate names. Some contacts had no email address. Phone numbers were written in different ways. Dates weren't consistent either. No single field could be trusted, on its own, to tell me that two records were the same person.&lt;/p&gt;

&lt;p&gt;So I used two rules to find possible matches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name plus email&lt;/li&gt;
&lt;li&gt;name plus phone, when there was no email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important word there is &lt;em&gt;possible&lt;/em&gt;. Neither combination is guaranteed to be unique across a whole dataset. Two people can share a name. A phone number can belong to an office rather than a person. So a match under those rules is a candidate, not proof.&lt;/p&gt;

&lt;p&gt;That distinction matters more than it sounds. If you treat a candidate as a confirmed match and merge automatically, you risk attaching a deal to the wrong contact. And a wrong link is much harder to notice later than a duplicate you can see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let the person who knows the data decide
&lt;/h2&gt;

&lt;p&gt;Where the rules couldn't settle it, the import had a review step. It showed the affected records to the person responsible for the data, so they could look at them before the import was confirmed.&lt;/p&gt;

&lt;p&gt;I like this approach because it's honest about what code can and can't know. The rules can narrow things down. They can't know that "J. Smith" with no email is the same John Smith your team spoke to last spring. The person who works with those contacts every day often can.&lt;/p&gt;

&lt;p&gt;There's a cost. A review step takes someone's time, and it slows the migration down. But I'd rather spend an afternoon of a person's attention than leave a decision like that to a guess.&lt;/p&gt;

&lt;p&gt;If you're planning a migration, budget for this. Someone on your side will need to look at the records the rules can't settle. That's not a sign the project is going badly. It's the project working as it should.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the new system for the people who'll use it
&lt;/h2&gt;

&lt;p&gt;The migration was the bigger problem, but it wasn't the only lesson.&lt;/p&gt;

&lt;p&gt;My first version of the CRM was designed around my own assumptions as a developer. I was thinking about what the system needed, not what the people using it were trying to get done. And the people using it weren't technical.&lt;/p&gt;

&lt;p&gt;Form validation wasn't strong enough, so input got through that should have been caught. Some interactions confused people. I've come to think of it this way: when a screen needs explaining, the screen is the bug.&lt;/p&gt;

&lt;p&gt;Validation matters twice as much after a migration. You've just spent real effort getting clean, connected data into the new system. Weak forms let the mess straight back in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three builds, one change in thinking
&lt;/h2&gt;

&lt;p&gt;Looking back across the three builds, the biggest change wasn't in the code. It was in where I started.&lt;/p&gt;

&lt;p&gt;At first, I started with what to build. By the end, I started with three questions. Who will use this? What does the existing data really contain? And what would be unacceptable to get wrong? For this agency, the answer to that last one was clear: a deal attached to the wrong contact.&lt;/p&gt;

&lt;p&gt;I don't have metrics to share from this project, and I'm not going to make any up. I also wouldn't call the result perfect, and I'm sure the old data held problems I never saw. But the way I approach this kind of work changed for good, and those three questions are where I'd start any migration now.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're planning your own Access migration
&lt;/h2&gt;

&lt;p&gt;Whether you do it yourself or hire someone, here's what I'd check before any code gets written:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Look at the real data early.&lt;/strong&gt; Not a description of it. An actual copy. Duplicates, blanks and odd formats show up fast once you look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide how record identity will work before building the importer.&lt;/strong&gt; How will each old record be linked to its new ID? If nobody can answer that, the migration isn't ready to start.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask how related records stay connected.&lt;/strong&gt; If the plan is "export the tables and import them", ask what happens to the links between contacts, deals, invoices or whatever your data holds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask what happens to records the rules can't match.&lt;/strong&gt; There should be a clear answer, and it shouldn't be "we merge them automatically and hope".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan time for someone on your side to review.&lt;/strong&gt; The person who knows the data is part of the migration, not just the person who signs it off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the new system with the people who'll use it every day.&lt;/strong&gt; If they need you to explain a screen, fix the screen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this is complicated. It's just easy to skip when you're focused on the new system and the old one feels like something to get rid of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to see more
&lt;/h2&gt;

&lt;p&gt;I'm Ayman Atif, a freelance software developer. I build business software for startups, small businesses and agencies, and a good share of that work starts with an old spreadsheet or database that nobody wants to touch.&lt;/p&gt;

&lt;p&gt;I wrote up the matching workflow and the trade-offs in more detail as a case study on my site: &lt;a href="https://ayman-atif.vercel.app/work/crm-data-migration/" rel="noopener noreferrer"&gt;CRM rebuild and Microsoft Access migration&lt;/a&gt;. You can see the rest of my work at &lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;ayman-atif.vercel.app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you've got an Access database that's holding your business together and you're wondering what moving it would involve, send me a short description of what's in it and what you need it to do. I'll tell you how I'd approach it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Publishing notes (not part of the article)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Target keyword:&lt;/strong&gt; migrate Microsoft Access database to web app&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secondary phrases used naturally in the text:&lt;/strong&gt; Access migration, Microsoft Access database, move Access to a web app, CRM data migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Suggested Medium tags (5):&lt;/strong&gt; Microsoft Access, Data Migration, Small Business, CRM, Software Development&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Canonical link plan:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Publish the article on your own site first, for example at &lt;code&gt;https://ayman-atif.vercel.app/blog/migrate-microsoft-access-database-to-web-app/&lt;/code&gt;, and add it to &lt;code&gt;sitemap.xml&lt;/code&gt;, &lt;code&gt;feed.xml&lt;/code&gt; and the blog index.&lt;/li&gt;
&lt;li&gt;Wait until Google has indexed it (check with URL Inspection in Search Console).&lt;/li&gt;
&lt;li&gt;Bring it into Medium with &lt;strong&gt;Import a story&lt;/strong&gt; (medium.com/p/import) using your site URL. Medium then sets the canonical link to your page automatically. If you paste it in by hand instead, open the story's &lt;strong&gt;More settings &amp;gt; Advanced settings&lt;/strong&gt; and set the canonical link to your page's URL before publishing.&lt;/li&gt;
&lt;li&gt;Keep the links to &lt;code&gt;/work/crm-data-migration/&lt;/code&gt; and the homepage in the Medium version, so readers (and search engines) find their way back to your site.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>backend</category>
      <category>webdev</category>
      <category>programming</category>
      <category>crm</category>
    </item>
    <item>
      <title>I Rebuilt a CRM 3 Times Because I Solved the Wrong Problem</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Fri, 16 Oct 2026 15:11:43 +0000</pubDate>
      <link>https://dev.to/a95yman/i-rebuilt-a-crm-3-times-because-i-solved-the-wrong-problem-4ifk</link>
      <guid>https://dev.to/a95yman/i-rebuilt-a-crm-3-times-because-i-solved-the-wrong-problem-4ifk</guid>
      <description>&lt;p&gt;The first time I built this CRM, I thought I understood the problem.&lt;/p&gt;

&lt;p&gt;I knew how to build forms, connect database tables, create features, and import data. I was a developer. I knew how software was supposed to work.&lt;/p&gt;

&lt;p&gt;But I had overlooked something important: &lt;strong&gt;knowing how to build software isn't the same as understanding the problem you're solving.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I ended up rebuilding the CRM three times before I got the solution right.&lt;/p&gt;

&lt;p&gt;And the biggest lesson had very little to do with writing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  I built it for developers, not users
&lt;/h2&gt;

&lt;p&gt;The CRM was built for an agency whose employees weren't necessarily technical.&lt;/p&gt;

&lt;p&gt;My first mistake was designing the application around what I thought people would naturally understand.&lt;/p&gt;

&lt;p&gt;If a field was labelled &lt;em&gt;Email&lt;/em&gt;, I expected users to enter an email address. If there was a button to expand a menu, I expected them to click it.&lt;/p&gt;

&lt;p&gt;So I didn't put enough thought into validation and interaction design.&lt;/p&gt;

&lt;p&gt;That assumption didn't hold up in practice.&lt;/p&gt;

&lt;p&gt;Users don't always follow the workflow a developer imagines. They make mistakes, misunderstand interfaces, and enter data in unexpected ways.&lt;/p&gt;

&lt;p&gt;A form shouldn't rely on someone having common sense about what belongs in each field. It should help them do the right thing and prevent avoidable mistakes.&lt;/p&gt;

&lt;p&gt;That was my first lesson: &lt;strong&gt;software needs to account for how people actually behave, not how we expect them to behave.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But the user interface wasn't the biggest problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real problem was hidden in the old data
&lt;/h2&gt;

&lt;p&gt;The agency already had its data in Microsoft Access. The goal was to move that data into the new CRM.&lt;/p&gt;

&lt;p&gt;I built an import feature for contacts and deals.&lt;/p&gt;

&lt;p&gt;At first glance, that sounds straightforward. Read the records from the old system, insert them into the new database, and you're done.&lt;/p&gt;

&lt;p&gt;Except contacts and deals weren't independent records. They were related.&lt;/p&gt;

&lt;p&gt;A deal belonged to a contact. When importing the data, the new CRM generated its own IDs. That was intentional: I didn't want to blindly reuse the identifiers from the legacy system.&lt;/p&gt;

&lt;p&gt;But I hadn't designed a reliable mapping between the old records and the new ones.&lt;/p&gt;

&lt;p&gt;Imagine importing 5,000 contacts and then importing 2,000 deals. How would the CRM know which newly created contact belonged to each deal?&lt;/p&gt;

&lt;p&gt;The original IDs weren't being preserved, and the old data didn't always provide a reliable alternative.&lt;/p&gt;

&lt;p&gt;I had created an import feature without fully accounting for the relationships that made the data meaningful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Importing records was easy. Preserving their relationships was the real challenge.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Names and emails weren't enough
&lt;/h2&gt;

&lt;p&gt;The old system had another complication.&lt;/p&gt;

&lt;p&gt;Some deals contained a person's name and email, but no reliable contact ID. That meant the importer had to figure out which contact a deal belonged to.&lt;/p&gt;

&lt;p&gt;What if two contacts had the same name?&lt;/p&gt;

&lt;p&gt;What if an email was missing?&lt;/p&gt;

&lt;p&gt;What if only a mobile phone number was available?&lt;/p&gt;

&lt;p&gt;Matching by name alone could connect a deal to the wrong person. Matching by email wasn't always possible. And assuming every record was clean would have made things worse.&lt;/p&gt;

&lt;p&gt;I needed a better way to identify candidate matches without pretending the data was more reliable than it really was.&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution: matching rules and human verification
&lt;/h2&gt;

&lt;p&gt;I changed the approach.&lt;/p&gt;

&lt;p&gt;Instead of treating every record as an isolated row to import, I introduced matching rules to help identify possible relationships.&lt;/p&gt;

&lt;p&gt;For example, I could compare a person's name and email. When an email wasn't available, I could use the name and phone number as an alternative.&lt;/p&gt;

&lt;p&gt;These combinations helped narrow down the possibilities, but they weren't a guarantee. Two records could still be ambiguous, and I didn't want the software making a potentially incorrect decision silently.&lt;/p&gt;

&lt;p&gt;So I added a review step.&lt;/p&gt;

&lt;p&gt;Before confirming the import, the system displayed the affected records in a pop-up so the responsible person could review them. Even when there were thousands of records, the process needed to make potential issues visible rather than blindly accepting questionable matches.&lt;/p&gt;

&lt;p&gt;The goal wasn't to pretend every record could be matched automatically.&lt;/p&gt;

&lt;p&gt;The goal was to make uncertainty visible and give the responsible person a chance to resolve it.&lt;/p&gt;

&lt;p&gt;I also had to clean up inconsistent date formats and other problems in the legacy data. Moving data between systems meant dealing with what actually existed in the old system, not what I wished had been there.&lt;/p&gt;

&lt;p&gt;There was no need to introduce AI into this problem. It required careful reasoning, matching rules, data validation, and human verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did I rebuild it three times?
&lt;/h2&gt;

&lt;p&gt;Because I started implementing before I had fully understood the problem.&lt;/p&gt;

&lt;p&gt;Each time, I had to go back, reconsider the approach, and start again. It cost time, but each attempt exposed assumptions I hadn't challenged.&lt;/p&gt;

&lt;p&gt;Looking back, I don't think the central lesson was that the CRM needed better code.&lt;/p&gt;

&lt;p&gt;It needed a better understanding of the requirements.&lt;/p&gt;

&lt;p&gt;I should have asked earlier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What makes a record identifiable when the source system has no reliable ID?&lt;/li&gt;
&lt;li&gt;How will relationships survive when destination IDs are generated from scratch?&lt;/li&gt;
&lt;li&gt;What happens when the source data is incomplete or inconsistent?&lt;/li&gt;
&lt;li&gt;Which decisions can the software make safely, and which require human review?&lt;/li&gt;
&lt;li&gt;How will we detect a questionable match before it becomes a data-integrity problem?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions would have changed the implementation from the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  I now think about the problem before the technology
&lt;/h2&gt;

&lt;p&gt;When I was more junior, I tended to jump straight into implementation. I'd think about the database, the forms, the features, and how to build them.&lt;/p&gt;

&lt;p&gt;Now, I try to slow down first.&lt;/p&gt;

&lt;p&gt;I look at the problem, the requirements, the constraints, and the limitations. I write down my assumptions and challenge them before turning them into code.&lt;/p&gt;

&lt;p&gt;That doesn't mean I can anticipate every complication. Real projects will always surprise you.&lt;/p&gt;

&lt;p&gt;But it does mean I can avoid building an entire solution on an assumption that should have been questioned at the start.&lt;/p&gt;

&lt;p&gt;The CRM taught me that lesson through three rebuilds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good engineering isn't just about making software work. It's about understanding what needs to work, for whom, and under which constraints, before you start building.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the author&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm Ayman Atif, a software engineer focused on building business software with .NET, C#, Python, and web technologies. I've been programming since 2014, and my experience has taught me that solving the right problem matters more than writing code quickly.&lt;/p&gt;

&lt;p&gt;🌐 &lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;https://ayman-atif.vercel.app/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>database</category>
      <category>design</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Rebuilt an Excel-Like Web App After the First Architecture Failed</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Fri, 02 Oct 2026 14:35:28 +0000</pubDate>
      <link>https://dev.to/a95yman/how-i-rebuilt-an-excel-like-web-app-after-the-first-architecture-failed-47lj</link>
      <guid>https://dev.to/a95yman/how-i-rebuilt-an-excel-like-web-app-after-the-first-architecture-failed-47lj</guid>
      <description>&lt;p&gt;Building an Excel-like application sounds straightforward until you start treating a spreadsheet like a normal database application.&lt;/p&gt;

&lt;p&gt;I learned that the hard way.&lt;/p&gt;

&lt;p&gt;I was building a web application where users could enter values into cells, organize information into rows and columns, and perform calculations using formulas.&lt;/p&gt;

&lt;p&gt;Then I added another requirement: version history.&lt;/p&gt;

&lt;p&gt;Users needed to be able to make changes, undo them, redo them, and return to previous versions of their work.&lt;/p&gt;

&lt;p&gt;That requirement exposed a much bigger problem with my original architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Approach
&lt;/h2&gt;

&lt;p&gt;My first instinct was to model the spreadsheet using traditional relational database structures.&lt;/p&gt;

&lt;p&gt;I created structures for things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rows&lt;/li&gt;
&lt;li&gt;columns&lt;/li&gt;
&lt;li&gt;cells&lt;/li&gt;
&lt;li&gt;formulas&lt;/li&gt;
&lt;li&gt;values&lt;/li&gt;
&lt;li&gt;relationships between spreadsheet elements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On paper, this seemed reasonable.&lt;/p&gt;

&lt;p&gt;A spreadsheet contains rows, columns, and cells, so representing those concepts directly in the database felt like a clean design.&lt;/p&gt;

&lt;p&gt;As the application grew, however, the dynamic was different.&lt;/p&gt;

&lt;p&gt;A spreadsheet is not just a collection of independent records.&lt;/p&gt;

&lt;p&gt;Changing one cell may affect another cell. Formulas can depend on coordinates elsewhere in the document. Rows and columns can move. Cells can be added, deleted, copied, recalculated, or restored.&lt;/p&gt;

&lt;p&gt;Then version history entered the equation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version History Exposed the Architecture Problem
&lt;/h2&gt;

&lt;p&gt;To support undo and redo, I needed to preserve previous states of the spreadsheet.&lt;/p&gt;

&lt;p&gt;With the original architecture, a single user action could affect multiple database records.&lt;/p&gt;

&lt;p&gt;Changing a formula might require updating a cell record, recalculating dependent values, maintaining relationships, and recording enough information to reconstruct the previous state.&lt;/p&gt;

&lt;p&gt;The number of moving pieces started to compound.&lt;/p&gt;

&lt;p&gt;I began running into problems such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;slow operations&lt;/li&gt;
&lt;li&gt;complicated synchronization&lt;/li&gt;
&lt;li&gt;unreliable undo and redo behavior&lt;/li&gt;
&lt;li&gt;difficult state reconstruction&lt;/li&gt;
&lt;li&gt;data inconsistencies&lt;/li&gt;
&lt;li&gt;changes being lost in edge cases&lt;/li&gt;
&lt;li&gt;increasingly complicated code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every new feature required touching several parts of the system.&lt;/p&gt;

&lt;p&gt;Eventually, maintaining the architecture became harder than adding features to it.&lt;/p&gt;

&lt;p&gt;That was the point where I had to reconsider my position.&lt;/p&gt;

&lt;p&gt;The problem was not one bad query or one buggy function. The underlying representation of the spreadsheet was making the application unnecessarily difficult to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Rebuilt the Data Model Around JSON
&lt;/h2&gt;

&lt;p&gt;Instead of trying to represent every spreadsheet concept as a separate relational entity, I rebuilt the system around a JSON representation of the spreadsheet state.&lt;/p&gt;

&lt;p&gt;The spreadsheet could now be represented as a structured document containing its cells, coordinates, values, formulas, and related state.&lt;/p&gt;

&lt;p&gt;Conceptually, a simplified version could look like this:&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;"cells"&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;"A1"&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;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&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;"A2"&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;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&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;"A3"&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;"formula"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"=A1+A2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;150&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;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;The real implementation was more involved, but the important change was the model.&lt;/p&gt;

&lt;p&gt;Coordinates such as &lt;code&gt;A1&lt;/code&gt;, &lt;code&gt;B4&lt;/code&gt;, or &lt;code&gt;F17&lt;/code&gt; became natural identifiers inside the spreadsheet state instead of requiring several database relationships just to locate a value.&lt;/p&gt;

&lt;p&gt;That simplified a large portion of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why JSON Fit This Problem Better
&lt;/h2&gt;

&lt;p&gt;Relational databases are extremely useful, but that does not mean every piece of application state needs to be decomposed into relational tables.&lt;/p&gt;

&lt;p&gt;In this case, the spreadsheet itself behaved more like a document.&lt;/p&gt;

&lt;p&gt;The cells belonged to the same logical unit. Their meaning depended heavily on their position and on the surrounding spreadsheet state.&lt;/p&gt;

&lt;p&gt;Representing that state together made several operations easier.&lt;/p&gt;

&lt;p&gt;Reading a spreadsheet became simpler.&lt;/p&gt;

&lt;p&gt;Saving it became simpler.&lt;/p&gt;

&lt;p&gt;Duplicating it became simpler.&lt;/p&gt;

&lt;p&gt;Restoring a previous version became much simpler.&lt;/p&gt;

&lt;p&gt;The formula engine also had a clearer structure to work with because spreadsheet coordinates could map directly to values and formulas.&lt;/p&gt;

&lt;p&gt;Instead of constantly translating between database IDs, row records, column records, and cell records, the application could work with spreadsheet coordinates directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Undo and Redo Became a Versioning Problem
&lt;/h2&gt;

&lt;p&gt;The biggest improvement was how I could think about history.&lt;/p&gt;

&lt;p&gt;Previously, undoing one user action meant determining which individual database records had changed and how to reverse each of those changes correctly.&lt;/p&gt;

&lt;p&gt;With the new architecture, I could preserve versions of the spreadsheet state.&lt;/p&gt;

&lt;p&gt;A simplified history might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Version 41
Version 42
Version 43
Version 44
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user made a change, a new version could be created.&lt;/p&gt;

&lt;p&gt;Undo could move back to an earlier state.&lt;/p&gt;

&lt;p&gt;Redo could restore the newer state when appropriate.&lt;/p&gt;

&lt;p&gt;There were still important engineering questions to solve, including storage growth, version retention, concurrency, and when versions should be created.&lt;/p&gt;

&lt;p&gt;But those were now isolated problems.&lt;/p&gt;

&lt;p&gt;I no longer had to reconstruct a spreadsheet from a long chain of unrelated database operations.&lt;/p&gt;

&lt;p&gt;That changed the equation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lesson Was Not "JSON Is Better Than SQL"
&lt;/h2&gt;

&lt;p&gt;That would be the wrong conclusion.&lt;/p&gt;

&lt;p&gt;The lesson was to choose a representation based on the behavior of the data.&lt;/p&gt;

&lt;p&gt;Relational tables still made sense for many parts of the application, such as users, permissions, document metadata, ownership, and other structured business data.&lt;/p&gt;

&lt;p&gt;The spreadsheet contents had different characteristics.&lt;/p&gt;

&lt;p&gt;They behaved like one interconnected document with dynamic coordinates and formulas.&lt;/p&gt;

&lt;p&gt;JSON fit that part of the problem better.&lt;/p&gt;

&lt;p&gt;Good architecture often comes down to drawing the boundary in the right place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sometimes Rebuilding Is Cheaper Than Continuing to Patch
&lt;/h2&gt;

&lt;p&gt;Rewriting software should not be the automatic response whenever a system becomes inconvenient.&lt;/p&gt;

&lt;p&gt;Usually, improving existing code is safer.&lt;/p&gt;

&lt;p&gt;But there is also a point where continuing to patch the wrong abstraction becomes wasteful.&lt;/p&gt;

&lt;p&gt;I reached that point with this application.&lt;/p&gt;

&lt;p&gt;Each workaround was making the next feature harder to implement. Fixes were increasing complexity instead of reducing it.&lt;/p&gt;

&lt;p&gt;I could have continued to put up with the architecture and keep adding special cases.&lt;/p&gt;

&lt;p&gt;Instead, I rebuilt the core model.&lt;/p&gt;

&lt;p&gt;That decision gave me a simpler system that was easier to maintain and easier to extend.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Do Differently Today
&lt;/h2&gt;

&lt;p&gt;If I were designing the system again from the beginning, I would identify the spreadsheet itself as a document much earlier.&lt;/p&gt;

&lt;p&gt;I would separate the system into concerns such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;relational data for users, permissions, and document metadata&lt;/li&gt;
&lt;li&gt;JSON-based spreadsheet state&lt;/li&gt;
&lt;li&gt;a dedicated formula calculation layer&lt;/li&gt;
&lt;li&gt;explicit document versioning&lt;/li&gt;
&lt;li&gt;controlled version retention&lt;/li&gt;
&lt;li&gt;validation before accepting saved state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would also design undo and redo as part of the architecture instead of treating them as features to bolt on later.&lt;/p&gt;

&lt;p&gt;That is one of the useful things about building real software.&lt;/p&gt;

&lt;p&gt;Sometimes the most valuable lesson is not learning how to implement your first design.&lt;/p&gt;

&lt;p&gt;It is learning when to abandon it.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Developer
&lt;/h2&gt;

&lt;p&gt;I'm &lt;strong&gt;Ayman Atif&lt;/strong&gt;, a software developer focused on business software, web applications, APIs, automation, reporting systems, and tools that replace complicated manual workflows.&lt;/p&gt;

&lt;p&gt;A large part of my work involves taking business processes that have grown around spreadsheets or manual operations and turning them into maintainable software.&lt;/p&gt;

&lt;p&gt;You can see more of my work and engineering projects on my portfolio:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ayman-atif.vercel.app" rel="noopener noreferrer"&gt;&lt;strong&gt;https://ayman-atif.vercel.app&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are looking for a freelance developer to build, improve, or fix a business application, you can contact me directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:a95yman@gmail.com"&gt;a95yman@gmail.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;WhatsApp:&lt;/strong&gt; +212 701-971272&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AI Could Build My Client’s App in a Week. Making It Reliable Took Me 3 Months.</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Mon, 21 Sep 2026 17:39:31 +0000</pubDate>
      <link>https://dev.to/a95yman/ai-could-build-my-clients-app-in-a-week-making-it-reliable-took-me-3-months-3clk</link>
      <guid>https://dev.to/a95yman/ai-could-build-my-clients-app-in-a-week-making-it-reliable-took-me-3-months-3clk</guid>
      <description>&lt;h1&gt;
  
  
  AI Could Build My Client’s App in a Week. Making It Reliable Took Me 3 Months.
&lt;/h1&gt;

&lt;p&gt;AI can generate software frighteningly fast.&lt;/p&gt;

&lt;p&gt;That is both its greatest advantage and one of the easiest traps to fall into.&lt;/p&gt;

&lt;p&gt;Earlier this year, I started working on a fairly complex client project. What began as an Excel spreadsheet gradually became a production platform with around 20 interconnected modules.&lt;/p&gt;

&lt;p&gt;I used AI heavily throughout development.&lt;/p&gt;

&lt;p&gt;Claude Sonnet and Opus helped with design and implementation. Codex Terra and Sol became my second pair of eyes for architecture, planning, and code review.&lt;/p&gt;

&lt;p&gt;The amount of code we could produce was incredible.&lt;/p&gt;

&lt;p&gt;At some points, it genuinely felt like the entire application could have been built in a week.&lt;/p&gt;

&lt;p&gt;Three months later, I understood why that would have been a terrible idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Version Is the Easy Part
&lt;/h2&gt;

&lt;p&gt;There is something addictive about building software with AI.&lt;/p&gt;

&lt;p&gt;You describe a feature.&lt;/p&gt;

&lt;p&gt;AI understands it.&lt;/p&gt;

&lt;p&gt;A few minutes later, you have hundreds of lines of working code.&lt;/p&gt;

&lt;p&gt;You test it.&lt;/p&gt;

&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;Then you move on to the next feature.&lt;/p&gt;

&lt;p&gt;And the next.&lt;/p&gt;

&lt;p&gt;And the next.&lt;/p&gt;

&lt;p&gt;For a small project, that workflow can be incredibly productive.&lt;/p&gt;

&lt;p&gt;But complexity compounds.&lt;/p&gt;

&lt;p&gt;By the time my project had grown into a large interconnected system, implementing a feature was no longer the difficult part.&lt;/p&gt;

&lt;p&gt;The difficult question became:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What did this feature accidentally break?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Feature Worked. Something Else Broke.
&lt;/h2&gt;

&lt;p&gt;This became one of the most frustrating patterns during development.&lt;/p&gt;

&lt;p&gt;I would implement a new feature and test it.&lt;/p&gt;

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

&lt;p&gt;Then I would discover that something seemingly unrelated had stopped working.&lt;/p&gt;

&lt;p&gt;Sometimes a change interfered with the application's Content Security Policy.&lt;/p&gt;

&lt;p&gt;Sometimes functionality elsewhere in the platform regressed.&lt;/p&gt;

&lt;p&gt;Sometimes the generated implementation duplicated logic that already existed somewhere else.&lt;/p&gt;

&lt;p&gt;AI was extremely good at solving the problem directly in front of it.&lt;/p&gt;

&lt;p&gt;But production software isn't a collection of isolated problems.&lt;/p&gt;

&lt;p&gt;Everything lives in a system.&lt;/p&gt;

&lt;p&gt;And once that system becomes sufficiently complex, local correctness isn't enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I Found a 20,000-Line File
&lt;/h2&gt;

&lt;p&gt;This was one of those moments where you look at your codebase and realize you have allowed something to go much too far.&lt;/p&gt;

&lt;p&gt;One file had grown to roughly &lt;strong&gt;20,000 lines of code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;AI didn't particularly care.&lt;/p&gt;

&lt;p&gt;It could keep reading the file, finding the relevant section, inserting another method, and continuing with its business.&lt;/p&gt;

&lt;p&gt;Technically, development could continue.&lt;/p&gt;

&lt;p&gt;Architecturally, it was becoming ridiculous.&lt;/p&gt;

&lt;p&gt;So I stopped generating features and dealt with the structure myself.&lt;/p&gt;

&lt;p&gt;I created the folders and files I wanted.&lt;/p&gt;

&lt;p&gt;I defined clearer responsibilities.&lt;/p&gt;

&lt;p&gt;Then I had AI help split the existing implementation across that structure.&lt;/p&gt;

&lt;p&gt;That experience changed how I use AI.&lt;/p&gt;

&lt;p&gt;I stopped asking only:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Does this work?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started asking:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Where does this belong?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those are very different questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scariest Problem Wasn't Messy Code
&lt;/h2&gt;

&lt;p&gt;Messy architecture is annoying.&lt;/p&gt;

&lt;p&gt;Data loss is different.&lt;/p&gt;

&lt;p&gt;At one point, I encountered a race condition that could result in data being lost.&lt;/p&gt;

&lt;p&gt;AI investigated it.&lt;/p&gt;

&lt;p&gt;A fix was implemented.&lt;/p&gt;

&lt;p&gt;The reasoning sounded convincing.&lt;/p&gt;

&lt;p&gt;The problem appeared solved.&lt;/p&gt;

&lt;p&gt;Except it wasn't.&lt;/p&gt;

&lt;p&gt;Eventually, I had to follow the execution path through myself, identify what was actually happening, and correct the underlying problem.&lt;/p&gt;

&lt;p&gt;That incident hammered home something important for me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confidence is not evidence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI can articulate an explanation beautifully while still being wrong.&lt;/p&gt;

&lt;p&gt;When you're building a toy project, that might cost you an afternoon.&lt;/p&gt;

&lt;p&gt;When you're building software for a client, that changes the equation.&lt;/p&gt;

&lt;p&gt;Someone else's business can depend on the code you're shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Also Doesn't Care If Your Product Slowly Loses Its Identity
&lt;/h2&gt;

&lt;p&gt;Another problem was subtler.&lt;/p&gt;

&lt;p&gt;Design inconsistency.&lt;/p&gt;

&lt;p&gt;When you're generating pages individually, each page can look perfectly acceptable.&lt;/p&gt;

&lt;p&gt;But put 15 or 20 of them together and suddenly:&lt;/p&gt;

&lt;p&gt;Buttons don't quite match.&lt;/p&gt;

&lt;p&gt;Spacing changes.&lt;/p&gt;

&lt;p&gt;Forms behave slightly differently.&lt;/p&gt;

&lt;p&gt;Modals follow different patterns.&lt;/p&gt;

&lt;p&gt;One section feels like it belongs to another application.&lt;/p&gt;

&lt;p&gt;No individual page looks terrible.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;product&lt;/strong&gt; looks inconsistent.&lt;/p&gt;

&lt;p&gt;So my role increasingly became less about typing code and more about monitoring the whole system.&lt;/p&gt;

&lt;p&gt;I had to catch discrepancies, regressions, security vulnerabilities, architectural problems, and UX inconsistencies.&lt;/p&gt;

&lt;p&gt;AI was doing more implementation.&lt;/p&gt;

&lt;p&gt;I was doing more supervision.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Eventually Split the Responsibilities Between AI Models
&lt;/h2&gt;

&lt;p&gt;Instead of asking one AI to do everything, I developed a workflow that worked much better for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codex became the planner and reviewer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used it to inspect architecture, challenge implementations, identify risks, and review changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude became the designer and implementer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sonnet and Opus handled much of the actual feature development and UI work.&lt;/p&gt;

&lt;p&gt;Then came testing.&lt;/p&gt;

&lt;p&gt;Automated tests.&lt;/p&gt;

&lt;p&gt;Manual tests.&lt;/p&gt;

&lt;p&gt;Browser testing.&lt;/p&gt;

&lt;p&gt;And, importantly, me actually using the application rather than assuming passing tests meant everything was fine.&lt;/p&gt;

&lt;p&gt;This slowed development down.&lt;/p&gt;

&lt;p&gt;That was a good thing.&lt;/p&gt;

&lt;p&gt;The goal was no longer to produce the most code possible.&lt;/p&gt;

&lt;p&gt;The goal was to confidently ship software.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Didn't Replace the Developer. It Changed the Developer's Job.
&lt;/h2&gt;

&lt;p&gt;This is probably my biggest takeaway from the entire project.&lt;/p&gt;

&lt;p&gt;I wrote dramatically less code manually than I would have a few years ago.&lt;/p&gt;

&lt;p&gt;But I didn't have dramatically less responsibility.&lt;/p&gt;

&lt;p&gt;In some ways, I had more.&lt;/p&gt;

&lt;p&gt;I needed to understand enough about the system to notice when AI was heading in the wrong direction.&lt;/p&gt;

&lt;p&gt;I needed to be assertive about architecture instead of letting generated code accumulate wherever convenient.&lt;/p&gt;

&lt;p&gt;I needed to recognize security regressions.&lt;/p&gt;

&lt;p&gt;I needed to test assumptions.&lt;/p&gt;

&lt;p&gt;I needed to reconsider my position when an implementation that looked elegant turned out to be fragile.&lt;/p&gt;

&lt;p&gt;And when something broke in production, I couldn't tell the client:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"The AI said it was fixed."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The responsibility was still mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Could AI Have Built It in a Week?
&lt;/h2&gt;

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

&lt;p&gt;At least something that looked like the finished application.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;Generating 20 modules isn't the same thing as building a dependable 20-module system.&lt;/p&gt;

&lt;p&gt;You can move incredibly fast with AI.&lt;/p&gt;

&lt;p&gt;But if every new feature creates two regressions, you're borrowing time from your future self.&lt;/p&gt;

&lt;p&gt;Eventually, you pay it back through debugging, refactoring, client complaints, longer working hours, and trying to identify which seemingly harmless change caused the latest problem.&lt;/p&gt;

&lt;p&gt;Speed is valuable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Controlled speed is much more valuable.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Months Later
&lt;/h2&gt;

&lt;p&gt;The project eventually reached the point where I was genuinely comfortable with it.&lt;/p&gt;

&lt;p&gt;It became a large production system with interconnected workflows, financial data, document generation, external sharing, contact management, reporting, and a substantial amount of business logic.&lt;/p&gt;

&lt;p&gt;More importantly, the development process itself became disciplined.&lt;/p&gt;

&lt;p&gt;AI remained heavily involved.&lt;/p&gt;

&lt;p&gt;I didn't respond to the problems by abandoning AI.&lt;/p&gt;

&lt;p&gt;I learned to use it differently.&lt;/p&gt;

&lt;p&gt;That's the part of AI-assisted development I think gets overlooked.&lt;/p&gt;

&lt;p&gt;The interesting question isn't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Can AI build an app?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Of course it can.&lt;/p&gt;

&lt;p&gt;The more useful question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Can you manage what AI builds when the application becomes complicated?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's where software engineering starts to matter again.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Documented the Full Experience
&lt;/h2&gt;

&lt;p&gt;I ended up turning the experience into a short case study covering the architecture problems, regressions, security issues, race condition, data loss, testing process, and the workflow I eventually developed between Claude, Codex, and myself.&lt;/p&gt;

&lt;p&gt;It's called &lt;strong&gt;I Built a 20-Module Business Valuation System With AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The title reveals more about the project than I wanted to discuss publicly here, while the case study goes much deeper into what actually happened during those three months.&lt;/p&gt;

&lt;p&gt;If you're using AI to build your own product or client software, you can read the full case study here:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://yaman95.gumroad.com/l/built-business-valuation-system-with-ai" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpublic-files.gumroad.com%2Fg53m6n3ei6v8pef8pdhxasw84ibj" height="451" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://yaman95.gumroad.com/l/built-business-valuation-system-with-ai" rel="noopener noreferrer" class="c-link"&gt;
            I Built a 20-Module Business Valuation System With AI
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            I used AI to help turn an Excel spreadsheet into a complete 20-module business valuation system. It looked like AI could make the project ridiculously fast...
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpublic-files.gumroad.com%2Fc3turpghsvintnl2nead2w8415ly" width="400" height="400"&gt;
          yaman95.gumroad.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I'm &lt;strong&gt;Ayman Atif&lt;/strong&gt;, a software developer who has spent years building business software. AI has changed how I work considerably.&lt;/p&gt;

&lt;p&gt;It hasn't taken responsibility out of the equation.&lt;/p&gt;

&lt;p&gt;If anything, it has made knowing &lt;strong&gt;when to intervene&lt;/strong&gt; more important than ever.&lt;/p&gt;

</description>
      <category>aiops</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>I've Been Programming for 14 Years. I Just Earned My First Microsoft Credential.</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Sat, 19 Sep 2026 15:10:42 +0000</pubDate>
      <link>https://dev.to/a95yman/ive-been-programming-for-14-years-i-just-earned-my-first-microsoft-credential-4gg4</link>
      <guid>https://dev.to/a95yman/ive-been-programming-for-14-years-i-just-earned-my-first-microsoft-credential-4gg4</guid>
      <description>&lt;p&gt;I’ve been programming for around 14 years.&lt;/p&gt;

&lt;p&gt;During all that time, I never really cared about certificates.&lt;/p&gt;

&lt;p&gt;I learned by building things.&lt;/p&gt;

&lt;p&gt;Web applications. Business software. APIs. Automation tools. Desktop applications. Projects that broke, projects that worked, and projects that forced me to learn things I thought I already understood.&lt;/p&gt;

&lt;p&gt;For years, that was enough for me.&lt;/p&gt;

&lt;p&gt;Then I decided to try something different.&lt;/p&gt;

&lt;p&gt;I gave myself three days to prepare for the &lt;strong&gt;Microsoft Applied Skills: Get started with classes, properties, and methods in C#&lt;/strong&gt; assessment.&lt;/p&gt;

&lt;p&gt;I passed.&lt;/p&gt;

&lt;p&gt;And it made me reconsider my position on technical credentials.&lt;/p&gt;

&lt;p&gt;I also turned the notes and exercises I personally used during those three days into a compact preparation resource for developers who want to follow a similar approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://yaman95.gumroad.com/l/microsoft-applied-skills-classes-methods-properties" rel="noopener noreferrer"&gt;https://yaman95.gumroad.com/l/microsoft-applied-skills-classes-methods-properties&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the interesting part isn’t the product.&lt;/p&gt;

&lt;p&gt;It’s what happened during those three days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I ignored programming certificates for so long
&lt;/h2&gt;

&lt;p&gt;My reasoning used to be pretty simple:&lt;/p&gt;

&lt;p&gt;If I can build software, why do I need a certificate telling people that I can build software?&lt;/p&gt;

&lt;p&gt;And there is still some truth to that.&lt;/p&gt;

&lt;p&gt;A credential cannot replace experience.&lt;/p&gt;

&lt;p&gt;It cannot replace the hours you spend debugging a problem that makes absolutely no sense until you finally find the tiny mistake causing it.&lt;/p&gt;

&lt;p&gt;It cannot replace shipping software.&lt;/p&gt;

&lt;p&gt;And it definitely cannot turn someone into an experienced developer overnight.&lt;/p&gt;

&lt;p&gt;So for most of my career, certificates were completely out of the equation.&lt;/p&gt;

&lt;p&gt;I based my argument on practical experience.&lt;/p&gt;

&lt;p&gt;Build things. Solve problems. Learn what you need. Repeat.&lt;/p&gt;

&lt;p&gt;But there was a weakness in that argument that I hadn't paid enough attention to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Experience and proof are not the same thing
&lt;/h2&gt;

&lt;p&gt;You might know C#.&lt;/p&gt;

&lt;p&gt;You might have used it professionally for years.&lt;/p&gt;

&lt;p&gt;But someone looking at your LinkedIn profile, resume, portfolio, or job application doesn't automatically know that.&lt;/p&gt;

&lt;p&gt;They have to take your word for it or spend time examining your work.&lt;/p&gt;

&lt;p&gt;A credential gives them another signal.&lt;/p&gt;

&lt;p&gt;Not proof that you're an amazing developer.&lt;/p&gt;

&lt;p&gt;Not proof that you're better than somebody without one.&lt;/p&gt;

&lt;p&gt;Just another piece of evidence.&lt;/p&gt;

&lt;p&gt;That distinction changed the equation for me.&lt;/p&gt;

&lt;p&gt;Microsoft describes Applied Skills credentials as a way to demonstrate practical ability through lab-based assessments involving real tasks rather than relying entirely on traditional knowledge questions.&lt;/p&gt;

&lt;p&gt;That caught my attention.&lt;/p&gt;

&lt;p&gt;So I decided to test one.&lt;/p&gt;

&lt;h2&gt;
  
  
  I chose C# classes, properties, and methods
&lt;/h2&gt;

&lt;p&gt;I started with:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microsoft Applied Skills: Get started with classes, properties, and methods in C#&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a beginner-level credential focused on fundamental object-oriented C# skills. Microsoft says the assessment evaluates your ability to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create classes, objects, and types&lt;/li&gt;
&lt;li&gt;work with properties&lt;/li&gt;
&lt;li&gt;work with methods&lt;/li&gt;
&lt;li&gt;call properties and methods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The assessment itself is an interactive lab, and Microsoft currently gives candidates up to two hours to complete it.&lt;/p&gt;

&lt;p&gt;You can see the official credential and assessment here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/credentials/applied-skills/get-started-with-classes-properties-and-methods-in-c-sharp/" rel="noopener noreferrer"&gt;https://learn.microsoft.com/en-us/credentials/applied-skills/get-started-with-classes-properties-and-methods-in-c-sharp/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I already had years of .NET experience, so my goal wasn't to learn C# from zero.&lt;/p&gt;

&lt;p&gt;My goal was different:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Could I organize what I needed to revise, hammer on my weak areas, practice them, and pass the assessment in three days?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I started preparing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three-day preparation
&lt;/h2&gt;

&lt;p&gt;I didn't want to spend three days passively reading documentation.&lt;/p&gt;

&lt;p&gt;I wanted something I could actually work through.&lt;/p&gt;

&lt;p&gt;So I built my own preparation folder.&lt;/p&gt;

&lt;p&gt;As I revised, I organized the concepts I needed, wrote examples, practiced the syntax, and focused on the areas covered by the assessment.&lt;/p&gt;

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

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

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

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

&lt;p&gt;Access modifiers.&lt;/p&gt;

&lt;p&gt;Nullable properties.&lt;/p&gt;

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

&lt;p&gt;Return values.&lt;/p&gt;

&lt;p&gt;Method overloading.&lt;/p&gt;

&lt;p&gt;Static classes and members.&lt;/p&gt;

&lt;p&gt;Object initializers.&lt;/p&gt;

&lt;p&gt;And the small details that are incredibly easy to understand when reading code but surprisingly easy to forget when you have to write the code yourself.&lt;/p&gt;

&lt;p&gt;That last part mattered.&lt;/p&gt;

&lt;p&gt;There's a big difference between:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Yeah, I know what that is.”&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“Give me an empty editor. I can implement it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Microsoft's own study guide reflects that practical scope, covering class creation, property access, nullable/default values, methods, overloads, static classes, object instantiation, and calling methods and properties.&lt;/p&gt;

&lt;p&gt;So I practiced.&lt;/p&gt;

&lt;p&gt;Then I took the assessment.&lt;/p&gt;

&lt;p&gt;And I passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The certificate wasn't actually the biggest benefit
&lt;/h2&gt;

&lt;p&gt;This was the counterintuitive part.&lt;/p&gt;

&lt;p&gt;I started because I wanted the credential.&lt;/p&gt;

&lt;p&gt;But the preparation itself ended up being just as valuable.&lt;/p&gt;

&lt;p&gt;When you've programmed for a long time, you accumulate knowledge from everywhere.&lt;/p&gt;

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

&lt;p&gt;Stack Overflow.&lt;/p&gt;

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

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

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

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

&lt;p&gt;Old codebases.&lt;/p&gt;

&lt;p&gt;New codebases.&lt;/p&gt;

&lt;p&gt;And increasingly, AI tools.&lt;/p&gt;

&lt;p&gt;You can become productive while still having little gaps in fundamentals that you haven't explicitly revisited in years.&lt;/p&gt;

&lt;p&gt;Preparing for an assessment forces you to organize that knowledge.&lt;/p&gt;

&lt;p&gt;You stop saying:&lt;/p&gt;

&lt;p&gt;“I basically know this.”&lt;/p&gt;

&lt;p&gt;You have to find out whether you actually do.&lt;/p&gt;

&lt;p&gt;That exposed a few things I needed to hammer on.&lt;/p&gt;

&lt;p&gt;And that's useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Certificates won't replace a portfolio
&lt;/h2&gt;

&lt;p&gt;I still wouldn't tell a developer to collect certificates and neglect everything else.&lt;/p&gt;

&lt;p&gt;If I had to weigh a credential against the ability to actually build software, practical ability would take precedence.&lt;/p&gt;

&lt;p&gt;Build projects.&lt;/p&gt;

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

&lt;p&gt;Learn debugging.&lt;/p&gt;

&lt;p&gt;Understand databases.&lt;/p&gt;

&lt;p&gt;Learn APIs.&lt;/p&gt;

&lt;p&gt;Understand how applications are structured.&lt;/p&gt;

&lt;p&gt;Break things.&lt;/p&gt;

&lt;p&gt;Fix them.&lt;/p&gt;

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

&lt;p&gt;But it doesn't have to be &lt;strong&gt;projects OR credentials&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can have both.&lt;/p&gt;

&lt;p&gt;A project demonstrates what you've built.&lt;/p&gt;

&lt;p&gt;Experience demonstrates what you've done professionally.&lt;/p&gt;

&lt;p&gt;A credential gives you an external signal that you've demonstrated a defined set of skills.&lt;/p&gt;

&lt;p&gt;Those things can complement each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Credentials can also give your learning a finish line
&lt;/h2&gt;

&lt;p&gt;This might be my favorite benefit.&lt;/p&gt;

&lt;p&gt;“Learn more C#” is a terrible goal.&lt;/p&gt;

&lt;p&gt;Where do you stop?&lt;/p&gt;

&lt;p&gt;What exactly do you study?&lt;/p&gt;

&lt;p&gt;How do you know when you've learned enough?&lt;/p&gt;

&lt;p&gt;An assessment gives the learning process boundaries.&lt;/p&gt;

&lt;p&gt;You have a defined skill set.&lt;/p&gt;

&lt;p&gt;You prepare.&lt;/p&gt;

&lt;p&gt;You practice.&lt;/p&gt;

&lt;p&gt;You take the assessment.&lt;/p&gt;

&lt;p&gt;You pass or you discover where you're weak.&lt;/p&gt;

&lt;p&gt;Either outcome gives you information.&lt;/p&gt;

&lt;p&gt;Instead of endlessly consuming tutorials, you're working toward something concrete.&lt;/p&gt;

&lt;p&gt;For me, that created momentum.&lt;/p&gt;

&lt;p&gt;I got the first credential.&lt;/p&gt;

&lt;p&gt;Now I want to keep rolling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does a Microsoft Applied Skills credential guarantee you a job?
&lt;/h2&gt;

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

&lt;p&gt;And I wouldn't sell that idea to anyone.&lt;/p&gt;

&lt;p&gt;A hiring decision involves experience, communication, technical ability, location, salary, the company, the role, competition, and sometimes plain luck.&lt;/p&gt;

&lt;p&gt;One credential doesn't erase that.&lt;/p&gt;

&lt;p&gt;But job hunting is rarely about finding one magical thing that gets you hired.&lt;/p&gt;

&lt;p&gt;It's about compounding signals.&lt;/p&gt;

&lt;p&gt;Your experience.&lt;/p&gt;

&lt;p&gt;Your projects.&lt;/p&gt;

&lt;p&gt;Your portfolio.&lt;/p&gt;

&lt;p&gt;Your GitHub.&lt;/p&gt;

&lt;p&gt;Your ability to articulate technical decisions.&lt;/p&gt;

&lt;p&gt;Your credentials.&lt;/p&gt;

&lt;p&gt;Your communication.&lt;/p&gt;

&lt;p&gt;One more credible signal can be useful.&lt;/p&gt;

&lt;p&gt;Microsoft itself positions Applied Skills as Microsoft-verified credentials designed to demonstrate specific practical skills, including through interactive, lab-based assessments.&lt;/p&gt;

&lt;p&gt;That's the way I'm looking at them now.&lt;/p&gt;

&lt;p&gt;Not as a replacement for experience.&lt;/p&gt;

&lt;p&gt;As another layer of proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  Would I pursue more Microsoft credentials?
&lt;/h2&gt;

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

&lt;p&gt;That's probably the biggest change in my position.&lt;/p&gt;

&lt;p&gt;After roughly 14 years of programming without paying much attention to certificates, I earned one and immediately understood why they can be useful.&lt;/p&gt;

&lt;p&gt;Not because the badge suddenly makes me a better programmer.&lt;/p&gt;

&lt;p&gt;It doesn't.&lt;/p&gt;

&lt;p&gt;The value was the process:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose a skill → revise it → practice it → identify gaps → prove it → move forward.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's a learning loop I can repeat.&lt;/p&gt;

&lt;p&gt;And I intend to.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're preparing for the same C# assessment
&lt;/h2&gt;

&lt;p&gt;During my three-day preparation, I created the material for myself first.&lt;/p&gt;

&lt;p&gt;It wasn't originally some grand product idea.&lt;/p&gt;

&lt;p&gt;It was my prep folder.&lt;/p&gt;

&lt;p&gt;After passing, I realized someone else preparing for the exact same assessment might find it useful.&lt;/p&gt;

&lt;p&gt;So I cleaned it up and turned it into a focused preparation resource:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C# Classes, Properties &amp;amp; Methods: My 3-Day Microsoft Applied Skills Prep&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It follows the concepts I revised before successfully completing the Microsoft Applied Skills assessment, with explanations, C# examples, and exercises.&lt;/p&gt;

&lt;p&gt;It's not a shortcut around learning the material.&lt;/p&gt;

&lt;p&gt;It's the material I used to make my own revision more focused.&lt;/p&gt;

&lt;p&gt;You can check it out here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://yaman95.gumroad.com/l/microsoft-applied-skills-classes-methods-properties" rel="noopener noreferrer"&gt;https://yaman95.gumroad.com/l/microsoft-applied-skills-classes-methods-properties&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And whether you use my notes or prepare completely on your own, I'd recommend giving the assessment a shot if these are skills you're currently learning.&lt;/p&gt;

&lt;p&gt;Three days ago, I had never earned a Microsoft credential.&lt;/p&gt;

&lt;p&gt;Now I have my first one.&lt;/p&gt;

&lt;p&gt;Time to work toward the next.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>webdev</category>
      <category>programming</category>
      <category>csharp</category>
    </item>
    <item>
      <title>I’ve Written C# for 10+ Years. These Are the Fundamentals I Still Revisit.</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Thu, 17 Sep 2026 21:23:10 +0000</pubDate>
      <link>https://dev.to/a95yman/ive-written-c-for-10-years-these-are-the-fundamentals-i-still-revisit-2f0h</link>
      <guid>https://dev.to/a95yman/ive-written-c-for-10-years-these-are-the-fundamentals-i-still-revisit-2f0h</guid>
      <description>&lt;p&gt;After more than 10 years of writing C#, I still look things up.&lt;/p&gt;

&lt;p&gt;Not obscure reflection APIs.&lt;/p&gt;

&lt;p&gt;Not some forgotten compiler flag.&lt;/p&gt;

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

&lt;p&gt;The difference between &lt;code&gt;const&lt;/code&gt; and &lt;code&gt;readonly&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What exactly happens when I pass a reference type to a method.&lt;/p&gt;

&lt;p&gt;Whether a LINQ query has actually executed yet.&lt;/p&gt;

&lt;p&gt;What lifetime a dependency should have.&lt;/p&gt;

&lt;p&gt;What happens to an exception inside an asynchronous operation.&lt;/p&gt;

&lt;p&gt;Things I learned years ago.&lt;/p&gt;

&lt;p&gt;Early in my career, I thought becoming an experienced developer meant eventually reaching a point where these questions disappeared.&lt;/p&gt;

&lt;p&gt;The opposite happened.&lt;/p&gt;

&lt;p&gt;The longer I worked with production software, the more I realized that a surprising number of difficult bugs come down to simple concepts behaving exactly as designed.&lt;/p&gt;

&lt;p&gt;You just misunderstood the design.&lt;/p&gt;

&lt;p&gt;I’m &lt;strong&gt;Ayman Atif&lt;/strong&gt;, a software engineer with more than 10 years of experience building business software, including CRM systems, financial applications, APIs, automation tools, reporting systems, and other production applications.&lt;/p&gt;

&lt;p&gt;C# and .NET have been a major part of that journey.&lt;/p&gt;

&lt;p&gt;You can see some of the software I’ve worked on and more about my background on my portfolio:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;Ayman Atif — Software Engineer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;These are some of the C# fundamentals I still revisit.&lt;/p&gt;

&lt;p&gt;Not because I forgot how to write C#.&lt;/p&gt;

&lt;p&gt;Because understanding them properly matters much more once the code has to survive production.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Value Types vs. Reference Types
&lt;/h2&gt;

&lt;p&gt;Every C# developer learns this fairly early.&lt;/p&gt;

&lt;p&gt;Value types contain their value.&lt;/p&gt;

&lt;p&gt;Reference types contain a reference to an object.&lt;/p&gt;

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

&lt;p&gt;Until code like this appears:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Customer&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="s"&gt;"John"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;UpdateCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&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;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;customer&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="s"&gt;"Ayman"&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;The output is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ayman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That surprises almost nobody with C# experience.&lt;/p&gt;

&lt;p&gt;But change the method slightly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Customer&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="s"&gt;"Ayman"&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;Now the original object hasn't been replaced.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because the reference itself was passed &lt;strong&gt;by value&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The method received a copy of that reference.&lt;/p&gt;

&lt;p&gt;Both references initially pointed to the same object. Reassigning the local copy doesn't change the caller's variable.&lt;/p&gt;

&lt;p&gt;That distinction sounds academic when you're learning C#.&lt;/p&gt;

&lt;p&gt;It stops being academic when you're debugging mutation across a large application.&lt;/p&gt;

&lt;p&gt;The useful question isn't simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this a class or a struct?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What exactly is being copied, and what exactly can this code mutate?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question has saved me considerably more time than memorizing definitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;const&lt;/code&gt; vs. &lt;code&gt;readonly&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is another one that looks almost too basic to revisit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;MaxRetries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;versus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;MaxRetries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;const&lt;/code&gt; value is known at compile time and is implicitly static.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;readonly&lt;/code&gt; field can be assigned when declared or inside a constructor.&lt;/p&gt;

&lt;p&gt;That means this is perfectly valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RetryPolicy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;MaxRetries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;RetryPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MaxRetries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maxRetries&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there's another reason I pay attention to &lt;code&gt;const&lt;/code&gt;, especially when libraries or multiple assemblies are involved.&lt;/p&gt;

&lt;p&gt;Constants can effectively be embedded into consuming code at compile time.&lt;/p&gt;

&lt;p&gt;Change a public constant in one assembly and an already-compiled consumer may continue using the old value until it is recompiled.&lt;/p&gt;

&lt;p&gt;That's the kind of detail that doesn't matter much in a console exercise.&lt;/p&gt;

&lt;p&gt;Across independently deployed components, it can matter a lot.&lt;/p&gt;

&lt;p&gt;The fundamental didn't change.&lt;/p&gt;

&lt;p&gt;The context did.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;var&lt;/code&gt; Is Not &lt;code&gt;dynamic&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I still see these mentally grouped together because both can make the declared type less obvious when reading a line of code.&lt;/p&gt;

&lt;p&gt;But they solve completely different problems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetCustomer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler still knows the type of &lt;code&gt;customer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetCustomer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is different.&lt;/p&gt;

&lt;p&gt;Certain type checks are deferred until runtime.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;var&lt;/code&gt;, this fails at compile time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ten"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;dynamic&lt;/code&gt;, this is allowed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;dynamic&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ten"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That flexibility comes with a tradeoff.&lt;/p&gt;

&lt;p&gt;You are moving some protection from compile time to runtime.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;var&lt;/code&gt; is mostly about letting the compiler infer a type.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dynamic&lt;/code&gt; changes how operations on that value are resolved.&lt;/p&gt;

&lt;p&gt;Small distinction.&lt;/p&gt;

&lt;p&gt;Very different consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;out&lt;/code&gt;, and &lt;code&gt;in&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;These keywords are easy to memorize and surprisingly easy to become fuzzy about when you don't use them regularly.&lt;/p&gt;

&lt;p&gt;I periodically revisit them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ref&lt;/code&gt; passes a variable by reference and expects it to already be initialized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ref&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;number&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;&lt;code&gt;out&lt;/code&gt; also passes by reference, but the called method must assign a value before returning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;TryGetAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&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="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;age&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;&lt;code&gt;in&lt;/code&gt; passes an argument by reference while preventing the method from assigning to the parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;LargeStruct&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// data cannot be reassigned here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax isn't the interesting part.&lt;/p&gt;

&lt;p&gt;The interesting part is recognizing what these keywords communicate about ownership, mutation, and intent.&lt;/p&gt;

&lt;p&gt;Most code doesn't need them.&lt;/p&gt;

&lt;p&gt;That's exactly why they're worth revisiting before using them casually.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. LINQ: When Does This Code Actually Run?
&lt;/h2&gt;

&lt;p&gt;LINQ is one of my favorite parts of C#.&lt;/p&gt;

&lt;p&gt;It is also very easy to write code that looks simpler than what it actually does.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;activeCustomers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Has the filtering happened?&lt;/p&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Many LINQ operations use deferred execution.&lt;/p&gt;

&lt;p&gt;The query may execute when you enumerate it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;activeCustomers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or when you materialize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;activeCustomers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That difference becomes much more important with Entity Framework Core.&lt;/p&gt;

&lt;p&gt;This:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;isn't the same thing as immediately loading every matching customer into memory.&lt;/p&gt;

&lt;p&gt;You're building a query.&lt;/p&gt;

&lt;p&gt;Then something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;causes it to execute against the database.&lt;/p&gt;

&lt;p&gt;When debugging LINQ or EF Core code, one of my first questions is often:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At what point does this query actually execute?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How many times does it execute?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those two questions can expose some nasty performance problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; Don't Mean "Create Another Thread"
&lt;/h2&gt;

&lt;p&gt;This is probably one of the most important C# fundamentals to understand properly.&lt;/p&gt;

&lt;p&gt;I used to mentally associate asynchronous programming much more closely with threads.&lt;/p&gt;

&lt;p&gt;That's an easy mental model to fall back on.&lt;/p&gt;

&lt;p&gt;But:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does not simply mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create another thread and make it wait for the HTTP request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For I/O-bound work, the whole point is that a thread doesn't need to sit there blocked while the external operation completes.&lt;/p&gt;

&lt;p&gt;When the operation finishes, execution can continue.&lt;/p&gt;

&lt;p&gt;That's incredibly important for server applications.&lt;/p&gt;

&lt;p&gt;Imagine an ASP.NET Core API handling hundreds of requests that are all waiting for databases, HTTP APIs, file operations, or other I/O.&lt;/p&gt;

&lt;p&gt;Blocking threads while they wait can eat up resources quickly.&lt;/p&gt;

&lt;p&gt;Asynchronous I/O allows those resources to be used more effectively.&lt;/p&gt;

&lt;p&gt;But there is another lesson here:&lt;/p&gt;

&lt;p&gt;Making a method &lt;code&gt;async&lt;/code&gt; doesn't magically make the application scalable.&lt;/p&gt;

&lt;p&gt;If you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;SomeAsyncOperation&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="nf"&gt;SomeAsyncOperation&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you are blocking again.&lt;/p&gt;

&lt;p&gt;Understanding &lt;strong&gt;why&lt;/strong&gt; async works matters much more than knowing where to type &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. &lt;code&gt;IDisposable&lt;/code&gt; and &lt;code&gt;using&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I learned &lt;code&gt;using&lt;/code&gt; a long time ago.&lt;/p&gt;

&lt;p&gt;I appreciate it much more now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the scope ends, &lt;code&gt;Dispose()&lt;/code&gt; is called.&lt;/p&gt;

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

&lt;p&gt;But the underlying lesson is about resource ownership.&lt;/p&gt;

&lt;p&gt;Some objects represent resources that shouldn't simply be left for garbage collection to eventually deal with.&lt;/p&gt;

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

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

&lt;p&gt;Database-related resources.&lt;/p&gt;

&lt;p&gt;Network resources.&lt;/p&gt;

&lt;p&gt;Native handles.&lt;/p&gt;

&lt;p&gt;And plenty of other things.&lt;/p&gt;

&lt;p&gt;Modern C# makes the syntax extremely clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CreateConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax is almost boring.&lt;/p&gt;

&lt;p&gt;The responsibility behind it isn't.&lt;/p&gt;

&lt;p&gt;When working with something disposable, I want to know:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who owns this resource?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who is responsible for disposing it?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That becomes especially important once dependency injection enters the equation.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Dependency Injection Lifetimes
&lt;/h2&gt;

&lt;p&gt;If you work with ASP.NET Core, you've probably seen these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's tempting to memorize them as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Transient = always new
Scoped = once per request
Singleton = one
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a useful starting point.&lt;/p&gt;

&lt;p&gt;It isn't enough.&lt;/p&gt;

&lt;p&gt;The real question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long should this object live, and what does it depend on?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A singleton depending on state that belongs to an individual request should immediately make you suspicious.&lt;/p&gt;

&lt;p&gt;A service holding mutable shared state needs careful thought.&lt;/p&gt;

&lt;p&gt;An Entity Framework &lt;code&gt;DbContext&lt;/code&gt; has lifetime expectations of its own.&lt;/p&gt;

&lt;p&gt;These problems often don't look dramatic in code.&lt;/p&gt;

&lt;p&gt;The registrations might occupy three lines in &lt;code&gt;Program.cs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The bug can appear somewhere completely different.&lt;/p&gt;

&lt;p&gt;That's why I revisit service lifetimes instead of assuming that because I've used dependency injection for years, every lifetime decision is obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Entity Framework Core Tracking
&lt;/h2&gt;

&lt;p&gt;EF Core makes database work pleasantly simple.&lt;/p&gt;

&lt;p&gt;Sometimes too simple.&lt;/p&gt;

&lt;p&gt;You can write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and move on.&lt;/p&gt;

&lt;p&gt;But what is EF Core doing with those entities afterward?&lt;/p&gt;

&lt;p&gt;By default, queries returning entities are generally tracking them.&lt;/p&gt;

&lt;p&gt;That's useful when you intend to modify them and save the changes.&lt;/p&gt;

&lt;p&gt;If you're loading data purely for reading, tracking may be unnecessary.&lt;/p&gt;

&lt;p&gt;That's where something like this becomes useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm not suggesting throwing &lt;code&gt;AsNoTracking()&lt;/code&gt; onto every query.&lt;/p&gt;

&lt;p&gt;That would just replace one habit with another.&lt;/p&gt;

&lt;p&gt;The point is understanding what the framework is doing for you.&lt;/p&gt;

&lt;p&gt;Abstractions are useful because they hide complexity.&lt;/p&gt;

&lt;p&gt;Experienced developers still need to know enough about the hidden complexity to recognize when it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Exceptions: Catching Everything Isn't Defensive Programming
&lt;/h2&gt;

&lt;p&gt;This looks safe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;ProcessOrder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;You've taken an error and made it invisible.&lt;/p&gt;

&lt;p&gt;A slightly more sophisticated version isn't necessarily much better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;ProcessOrder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&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;What happens now?&lt;/p&gt;

&lt;p&gt;Can the application continue safely?&lt;/p&gt;

&lt;p&gt;Should the operation be retried?&lt;/p&gt;

&lt;p&gt;Should the caller know it failed?&lt;/p&gt;

&lt;p&gt;Should it be logged?&lt;/p&gt;

&lt;p&gt;Is this actually the correct layer to handle the exception?&lt;/p&gt;

&lt;p&gt;Catching an exception should have a reason.&lt;/p&gt;

&lt;p&gt;Sometimes the correct decision is to handle it.&lt;/p&gt;

&lt;p&gt;Sometimes it's to translate it into something meaningful at an application boundary.&lt;/p&gt;

&lt;p&gt;Sometimes it's to log it.&lt;/p&gt;

&lt;p&gt;And sometimes the correct decision is to let it propagate.&lt;/p&gt;

&lt;p&gt;I don't base exception handling on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can this throw?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I prefer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can this layer do something meaningful if it throws?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That produces very different code.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Collections Are Part of Your Design
&lt;/h2&gt;

&lt;p&gt;Need several objects?&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's often perfectly fine.&lt;/p&gt;

&lt;p&gt;But not always.&lt;/p&gt;

&lt;p&gt;If you're repeatedly checking whether something exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&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;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then the choice between something like a &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; and a &lt;code&gt;HashSet&amp;lt;T&amp;gt;&lt;/code&gt; can become important as the collection grows.&lt;/p&gt;

&lt;p&gt;If you're constantly retrieving values by key, a &lt;code&gt;Dictionary&amp;lt;TKey, TValue&amp;gt;&lt;/code&gt; may better represent the problem.&lt;/p&gt;

&lt;p&gt;The data structure communicates something about the operation you care about.&lt;/p&gt;

&lt;p&gt;A list says sequence.&lt;/p&gt;

&lt;p&gt;A dictionary says lookup by key.&lt;/p&gt;

&lt;p&gt;A set says uniqueness and membership.&lt;/p&gt;

&lt;p&gt;You don't need to obsess over theoretical complexity every time you create a collection.&lt;/p&gt;

&lt;p&gt;But once data grows or an operation sits inside a hot loop, choosing the right structure can change the equation considerably.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. &lt;code&gt;static&lt;/code&gt; Deserves More Thought Than It Gets
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;static&lt;/code&gt; looks harmless.&lt;/p&gt;

&lt;p&gt;Sometimes it is exactly what you need.&lt;/p&gt;

&lt;p&gt;But static state can quietly create coupling.&lt;/p&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CurrentUser&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That might look convenient in a tiny application.&lt;/p&gt;

&lt;p&gt;Now introduce concurrent users.&lt;/p&gt;

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

&lt;p&gt;Parallel execution.&lt;/p&gt;

&lt;p&gt;Different request contexts.&lt;/p&gt;

&lt;p&gt;Suddenly that convenience needs much more scrutiny.&lt;/p&gt;

&lt;p&gt;This doesn't mean static members are bad.&lt;/p&gt;

&lt;p&gt;Far from it.&lt;/p&gt;

&lt;p&gt;Pure utility functionality can make perfect sense as static behavior.&lt;/p&gt;

&lt;p&gt;Constants and genuinely application-wide state have legitimate uses too.&lt;/p&gt;

&lt;p&gt;The important distinction is between:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;functionality that doesn't require instance state&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mutable global state that happens to be convenient.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those are not the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Equality Is More Complicated Than &lt;code&gt;==&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is another fundamental that becomes interesting once domain models become more complicated.&lt;/p&gt;

&lt;p&gt;What does it mean for two objects to be equal?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Customer&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="m"&gt;10&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="s"&gt;"Ayman"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;customer2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Customer&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="m"&gt;10&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="s"&gt;"Ayman"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Are these equal?&lt;/p&gt;

&lt;p&gt;They contain the same values.&lt;/p&gt;

&lt;p&gt;But they're two separate instances.&lt;/p&gt;

&lt;p&gt;Depending on the type and how equality is implemented, your answer may differ.&lt;/p&gt;

&lt;p&gt;Then records enter the picture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Records give you value-oriented equality semantics that can be extremely useful for certain models.&lt;/p&gt;

&lt;p&gt;The broader lesson isn't "use records."&lt;/p&gt;

&lt;p&gt;It's to define what equality actually means for the thing you're modeling.&lt;/p&gt;

&lt;p&gt;Two references being different doesn't necessarily mean the domain considers the values different.&lt;/p&gt;

&lt;p&gt;And two references pointing to the same object doesn't necessarily tell you anything useful about business identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. Nullability Is Better When You Treat It as Design Information
&lt;/h2&gt;

&lt;p&gt;Nullable reference types were one of those C# additions that initially looked like extra compiler noise to some developers.&lt;/p&gt;

&lt;p&gt;But consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;versus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;?&lt;/code&gt; communicates something.&lt;/p&gt;

&lt;p&gt;The absence of a value is expected.&lt;/p&gt;

&lt;p&gt;That's not merely a compiler detail.&lt;/p&gt;

&lt;p&gt;It's information about your model.&lt;/p&gt;

&lt;p&gt;If a customer must always have an email address, model that intentionally.&lt;/p&gt;

&lt;p&gt;If a middle name is optional, model that intentionally too.&lt;/p&gt;

&lt;p&gt;The goal isn't to silence every nullable warning as quickly as possible.&lt;/p&gt;

&lt;p&gt;The goal is to make the compiler help you identify places where your assumptions about missing values may be wrong.&lt;/p&gt;

&lt;p&gt;The more accurately the type system describes reality, the less defensive guessing the rest of your code needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern I Keep Seeing
&lt;/h2&gt;

&lt;p&gt;After years of C# and .NET development, I've noticed something.&lt;/p&gt;

&lt;p&gt;Junior developers often worry about not knowing enough advanced features.&lt;/p&gt;

&lt;p&gt;Experienced developers often worry about misunderstanding simple ones.&lt;/p&gt;

&lt;p&gt;That's a very different concern.&lt;/p&gt;

&lt;p&gt;A clever language feature might save you five lines of code.&lt;/p&gt;

&lt;p&gt;A misunderstanding of object lifetime, async behavior, query execution, mutation, equality, or resource ownership can create a bug that takes hours to find.&lt;/p&gt;

&lt;p&gt;Sometimes days.&lt;/p&gt;

&lt;p&gt;Production software has repeatedly hammered that lesson into me.&lt;/p&gt;

&lt;p&gt;The code that causes the worst problems isn't always complicated.&lt;/p&gt;

&lt;p&gt;Sometimes it's completely ordinary code built on one incorrect assumption.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Don't Outgrow the Fundamentals
&lt;/h2&gt;

&lt;p&gt;Ten years ago, revisiting C# fundamentals would have felt like going backward.&lt;/p&gt;

&lt;p&gt;Today I see it differently.&lt;/p&gt;

&lt;p&gt;I don't need to prove that I remember every detail of the language without checking documentation.&lt;/p&gt;

&lt;p&gt;I need to make good engineering decisions.&lt;/p&gt;

&lt;p&gt;Sometimes that means opening the documentation.&lt;/p&gt;

&lt;p&gt;Sometimes it means writing a tiny console application to verify behavior.&lt;/p&gt;

&lt;p&gt;Sometimes it means reconsidering my position on a pattern I've used for years.&lt;/p&gt;

&lt;p&gt;And sometimes it means returning to something I supposedly "learned" a decade ago and realizing I understand it differently now.&lt;/p&gt;

&lt;p&gt;That's not going backward.&lt;/p&gt;

&lt;p&gt;That's what experience does.&lt;/p&gt;

&lt;p&gt;You stop collecting syntax.&lt;/p&gt;

&lt;p&gt;You start understanding consequences.&lt;/p&gt;

&lt;p&gt;And in a language as mature and capable as C#, there is always another layer underneath something you thought you already knew.&lt;/p&gt;




&lt;p&gt;I'm &lt;strong&gt;Ayman Atif&lt;/strong&gt;, a software engineer with 10+ years of experience building and maintaining production software with &lt;strong&gt;C#, .NET, Python, SQL, APIs, automation, financial systems, CRM platforms, and business applications&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I write about lessons from building real software, including the mistakes, architecture decisions, performance problems, and fundamentals that become more important as applications grow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portfolio and projects:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;Ayman Atif — Software Engineer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>programming</category>
      <category>backend</category>
    </item>
    <item>
      <title>How I Learned Odoo Development From Scratch and Got the Job in 30 Days</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Tue, 15 Sep 2026 15:40:21 +0000</pubDate>
      <link>https://dev.to/a95yman/how-i-learned-odoo-development-from-scratch-and-got-the-job-in-30-days-1ld8</link>
      <guid>https://dev.to/a95yman/how-i-learned-odoo-development-from-scratch-and-got-the-job-in-30-days-1ld8</guid>
      <description>&lt;p&gt;In 2024, I applied for an Odoo Developer role even though I had never worked with Odoo before.&lt;/p&gt;

&lt;p&gt;Not “a little experience.”&lt;/p&gt;

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

&lt;p&gt;I was already a software engineer with more than 10 years of experience building business software, primarily with .NET, alongside Python, APIs, automation, CRM systems, financial software, and production web applications.&lt;/p&gt;

&lt;p&gt;My name is Ayman Atif, and I have been developing software since 2014.&lt;/p&gt;

&lt;p&gt;You can see more of my work and background on my portfolio:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;https://ayman-atif.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the time, I was looking for a second income stream and applying for remote development jobs.&lt;/p&gt;

&lt;p&gt;Then I found a job post asking for:&lt;/p&gt;

&lt;p&gt;Python&lt;br&gt;
PostgreSQL&lt;br&gt;
Odoo&lt;/p&gt;

&lt;p&gt;Python was familiar territory.&lt;/p&gt;

&lt;p&gt;PostgreSQL was not a problem either.&lt;/p&gt;

&lt;p&gt;Odoo was the strange part.&lt;/p&gt;

&lt;p&gt;I had never developed an Odoo module.&lt;/p&gt;

&lt;p&gt;I had never built an Odoo application.&lt;/p&gt;

&lt;p&gt;I had barely paid attention to the platform before that job post appeared.&lt;/p&gt;

&lt;p&gt;Normally, that sounds like a job you skip.&lt;/p&gt;

&lt;p&gt;I applied anyway.&lt;/p&gt;

&lt;p&gt;That decision eventually led to one of the most useful lessons I have learned in my software career:&lt;/p&gt;

&lt;p&gt;You do not always need to know the technology before you apply. Sometimes you need to prove that you know how to learn it.&lt;/p&gt;

&lt;p&gt;This article explains exactly how I approached that month, what I studied, what I deliberately skipped, and the system I now use whenever I need to learn an unfamiliar technology quickly.&lt;/p&gt;

&lt;p&gt;I Did Not Pretend to Be an Odoo Developer&lt;/p&gt;

&lt;p&gt;This part matters.&lt;/p&gt;

&lt;p&gt;I did not rewrite my resume and pretend that I had Odoo experience.&lt;/p&gt;

&lt;p&gt;I did not exaggerate my background.&lt;/p&gt;

&lt;p&gt;I did not create a fake portfolio project and present myself as something I was not.&lt;/p&gt;

&lt;p&gt;Instead, I based my argument on something I could actually defend.&lt;/p&gt;

&lt;p&gt;I told the employer that I had experience transitioning into unfamiliar technical areas quickly.&lt;/p&gt;

&lt;p&gt;And I backed that claim with a real example.&lt;/p&gt;

&lt;p&gt;Before this Odoo opportunity, I had worked on a news aggregation system that automatically transformed news content into videos.&lt;/p&gt;

&lt;p&gt;That project involved AI-related development.&lt;/p&gt;

&lt;p&gt;At the time, I did not come from an AI background.&lt;/p&gt;

&lt;p&gt;I had to understand an unfamiliar technical area, experiment, solve practical problems, and contribute to a working system.&lt;/p&gt;

&lt;p&gt;That was the evidence behind my claim.&lt;/p&gt;

&lt;p&gt;My message was essentially:&lt;/p&gt;

&lt;p&gt;I may not know Odoo yet, but I have already demonstrated that I can enter unfamiliar technical territory and become productive.&lt;/p&gt;

&lt;p&gt;The letter was not particularly fancy.&lt;/p&gt;

&lt;p&gt;It was not full of corporate language.&lt;/p&gt;

&lt;p&gt;It was just honest.&lt;/p&gt;

&lt;p&gt;And apparently, that was enough to get their attention.&lt;/p&gt;

&lt;p&gt;The Employer Challenged My Claim&lt;/p&gt;

&lt;p&gt;Eventually, they came back with a simple challenge.&lt;/p&gt;

&lt;p&gt;If I believed I could transition into Odoo quickly, I had to prove it.&lt;/p&gt;

&lt;p&gt;I was given roughly one month to learn the basics of Odoo development and build a project.&lt;/p&gt;

&lt;p&gt;The project was an optical module for a clinic.&lt;/p&gt;

&lt;p&gt;That changed the equation completely.&lt;/p&gt;

&lt;p&gt;I no longer needed to convince someone with words.&lt;/p&gt;

&lt;p&gt;I had a real technical target.&lt;/p&gt;

&lt;p&gt;Thirty days.&lt;/p&gt;

&lt;p&gt;A technology I did not know.&lt;/p&gt;

&lt;p&gt;A real project at the end.&lt;/p&gt;

&lt;p&gt;This was exactly the kind of situation where spending three weeks preparing to start would have been a mistake.&lt;/p&gt;

&lt;p&gt;So I did not approach it like a traditional course.&lt;/p&gt;

&lt;p&gt;I approached it like a developer entering an unfamiliar codebase.&lt;/p&gt;

&lt;p&gt;Why I Did Not Start With a Long Odoo Course&lt;/p&gt;

&lt;p&gt;There is nothing inherently wrong with courses.&lt;/p&gt;

&lt;p&gt;The problem was time.&lt;/p&gt;

&lt;p&gt;I did not want to spend several hours watching someone explain how a manifest file works before I had even touched one.&lt;/p&gt;

&lt;p&gt;I needed context first.&lt;/p&gt;

&lt;p&gt;So instead of making tutorials the center of my learning process, I went directly to existing Odoo modules.&lt;/p&gt;

&lt;p&gt;I opened GitHub repositories.&lt;/p&gt;

&lt;p&gt;I looked at small modules.&lt;/p&gt;

&lt;p&gt;I scanned their structure.&lt;/p&gt;

&lt;p&gt;I wanted to see what kept repeating.&lt;/p&gt;

&lt;p&gt;Very quickly, certain pieces appeared again and again:&lt;/p&gt;

&lt;p&gt;manifest files&lt;br&gt;
models&lt;br&gt;
XML views&lt;br&gt;
module initialization&lt;br&gt;
Python classes&lt;br&gt;
fields&lt;br&gt;
actions&lt;br&gt;
menus&lt;/p&gt;

&lt;p&gt;That gave me a map.&lt;/p&gt;

&lt;p&gt;The important difference in my case was the order.&lt;/p&gt;

&lt;p&gt;I did not try to understand everything before writing anything.&lt;/p&gt;

&lt;p&gt;I touched the code first.&lt;/p&gt;

&lt;p&gt;My Learning Loop Was Simple&lt;/p&gt;

&lt;p&gt;My basic loop looked like this:&lt;/p&gt;

&lt;p&gt;Inspect → Rebuild → Modify → Break → Debug → Repeat&lt;/p&gt;

&lt;p&gt;I would open a small module and inspect how it was structured.&lt;/p&gt;

&lt;p&gt;Then I would rebuild part of it.&lt;/p&gt;

&lt;p&gt;Then I would change something.&lt;/p&gt;

&lt;p&gt;Then something would inevitably break.&lt;/p&gt;

&lt;p&gt;And that failure forced me to understand the part I had just touched.&lt;/p&gt;

&lt;p&gt;This was much more useful to me than passively watching hours of content.&lt;/p&gt;

&lt;p&gt;There is a counterintuitive part here.&lt;/p&gt;

&lt;p&gt;At the beginning, I memorized syntax.&lt;/p&gt;

&lt;p&gt;Sometimes almost word for word.&lt;/p&gt;

&lt;p&gt;Not because memorization was the end goal.&lt;/p&gt;

&lt;p&gt;Because it reduced friction.&lt;/p&gt;

&lt;p&gt;When you enter a completely unfamiliar framework, everything feels expensive.&lt;/p&gt;

&lt;p&gt;You forget the file names.&lt;/p&gt;

&lt;p&gt;You forget where things belong.&lt;/p&gt;

&lt;p&gt;You forget the syntax.&lt;/p&gt;

&lt;p&gt;You forget what calls what.&lt;/p&gt;

&lt;p&gt;If you memorize some of those mechanical pieces early, your brain has more room to understand the actual structure later.&lt;/p&gt;

&lt;p&gt;Understanding gradually replaced memorization.&lt;/p&gt;

&lt;p&gt;I Still Used YouTube, Just Not as the Main Learning Method&lt;/p&gt;

&lt;p&gt;I was also watching an Odoo development playlist.&lt;/p&gt;

&lt;p&gt;The playlist was titled:&lt;/p&gt;

&lt;p&gt;How To Configure Odoo15 With Pycharm || Odoo 15 Development Tutorials&lt;/p&gt;

&lt;p&gt;It contained around 189 videos.&lt;/p&gt;

&lt;p&gt;I watched somewhere around 60 of them.&lt;/p&gt;

&lt;p&gt;I did not sit down and consume all 189 videos before building anything.&lt;/p&gt;

&lt;p&gt;That would have defeated the purpose.&lt;/p&gt;

&lt;p&gt;I typically watched around two videos per day.&lt;/p&gt;

&lt;p&gt;Then I reinforced the concepts by working directly with modules.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;p&gt;Watching was supporting the work.&lt;/p&gt;

&lt;p&gt;The work was not supporting the watching.&lt;/p&gt;

&lt;p&gt;A lot of developers accidentally reverse that.&lt;/p&gt;

&lt;p&gt;They spend days collecting information, then tell themselves they will build something once they “know enough.”&lt;/p&gt;

&lt;p&gt;You can easily get stuck there.&lt;/p&gt;

&lt;p&gt;What I Focused on First&lt;/p&gt;

&lt;p&gt;I deliberately narrowed the scope.&lt;/p&gt;

&lt;p&gt;I did not try to become an Odoo expert.&lt;/p&gt;

&lt;p&gt;I needed to become useful.&lt;/p&gt;

&lt;p&gt;So I worked through the structure in roughly this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Manifest Files&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted to understand what defined a module.&lt;/p&gt;

&lt;p&gt;What did Odoo need in order to recognize it?&lt;/p&gt;

&lt;p&gt;What information belonged in the manifest?&lt;/p&gt;

&lt;p&gt;What did dependencies look like?&lt;/p&gt;

&lt;p&gt;What resources were loaded?&lt;/p&gt;

&lt;p&gt;I did not need deep architectural knowledge yet.&lt;/p&gt;

&lt;p&gt;I needed orientation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Models&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once the module structure felt less foreign, I moved into models.&lt;/p&gt;

&lt;p&gt;This part felt more natural because I already had years of programming experience.&lt;/p&gt;

&lt;p&gt;Classes, fields, business logic, relational data, validation, and procedural problem solving were not new concepts.&lt;/p&gt;

&lt;p&gt;The syntax was new.&lt;/p&gt;

&lt;p&gt;The thinking was not.&lt;/p&gt;

&lt;p&gt;That distinction is incredibly important when learning a new stack.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;XML Views&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then came the UI structure.&lt;/p&gt;

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

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

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

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

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

&lt;p&gt;XML initially created more friction because it was tied closely to Odoo’s conventions.&lt;/p&gt;

&lt;p&gt;But repetition made it predictable.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Modification&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once I understood a small module, I changed it.&lt;/p&gt;

&lt;p&gt;Rename a field.&lt;/p&gt;

&lt;p&gt;Add something.&lt;/p&gt;

&lt;p&gt;Remove something.&lt;/p&gt;

&lt;p&gt;Change a view.&lt;/p&gt;

&lt;p&gt;Alter behavior.&lt;/p&gt;

&lt;p&gt;This was where the learning became real.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Breaking the module was often more educational than successfully copying it.&lt;/p&gt;

&lt;p&gt;When something failed, I had to follow the argument through.&lt;/p&gt;

&lt;p&gt;Why did it fail?&lt;/p&gt;

&lt;p&gt;Was the model wrong?&lt;/p&gt;

&lt;p&gt;Was the XML wrong?&lt;/p&gt;

&lt;p&gt;Was the module not loaded correctly?&lt;/p&gt;

&lt;p&gt;Was the field missing?&lt;/p&gt;

&lt;p&gt;Was the dependency wrong?&lt;/p&gt;

&lt;p&gt;Debugging turned passive familiarity into usable knowledge.&lt;/p&gt;

&lt;p&gt;My Existing Programming Experience Did Most of the Heavy Lifting&lt;/p&gt;

&lt;p&gt;This is one reason I do not think developers should mentally reset themselves to zero when switching technologies.&lt;/p&gt;

&lt;p&gt;Your previous experience is not out of the equation.&lt;/p&gt;

&lt;p&gt;It transfers.&lt;/p&gt;

&lt;p&gt;I already understood:&lt;/p&gt;

&lt;p&gt;procedural programming&lt;br&gt;
object-oriented programming&lt;br&gt;
databases&lt;br&gt;
debugging&lt;br&gt;
business logic&lt;br&gt;
APIs&lt;br&gt;
application structure&lt;br&gt;
data flow&lt;br&gt;
software maintenance&lt;br&gt;
production constraints&lt;/p&gt;

&lt;p&gt;I was learning a framework, not relearning how computers work.&lt;/p&gt;

&lt;p&gt;That dramatically shortened the learning curve.&lt;/p&gt;

&lt;p&gt;The more fundamentals you already understand, the less a new framework feels like starting over.&lt;/p&gt;

&lt;p&gt;My Daily Schedule&lt;/p&gt;

&lt;p&gt;My schedule during that period was fairly structured.&lt;/p&gt;

&lt;p&gt;I worked from around:&lt;/p&gt;

&lt;p&gt;9:00 AM to 6:00 PM&lt;/p&gt;

&lt;p&gt;with approximately a one-hour break.&lt;/p&gt;

&lt;p&gt;Outside of the core work period, I kept reinforcing what I was learning.&lt;/p&gt;

&lt;p&gt;After I got the role, the intensity increased.&lt;/p&gt;

&lt;p&gt;For roughly the next month, I was doing something close to:&lt;/p&gt;

&lt;p&gt;8 hours working&lt;/p&gt;

&lt;p&gt;plus&lt;/p&gt;

&lt;p&gt;4 additional hours learning and practicing&lt;/p&gt;

&lt;p&gt;That is around 12 hours a day connected to the new technology.&lt;/p&gt;

&lt;p&gt;I would not recommend that schedule indefinitely.&lt;/p&gt;

&lt;p&gt;It is difficult to sustain.&lt;/p&gt;

&lt;p&gt;But for a short transition period, it accelerated the feedback loop enormously.&lt;/p&gt;

&lt;p&gt;Every day I was seeing real problems at work, then learning the concepts behind them afterward.&lt;/p&gt;

&lt;p&gt;Work generated questions.&lt;/p&gt;

&lt;p&gt;Study answered them.&lt;/p&gt;

&lt;p&gt;The next day generated better questions.&lt;/p&gt;

&lt;p&gt;That compounded quickly.&lt;/p&gt;

&lt;p&gt;What Happened After the First Month&lt;/p&gt;

&lt;p&gt;At the end of the month, I completed the challenge and earned the role.&lt;/p&gt;

&lt;p&gt;But getting the job was not the end of the learning process.&lt;/p&gt;

&lt;p&gt;That was really when the useful learning started.&lt;/p&gt;

&lt;p&gt;During the first month, I was mainly getting comfortable with:&lt;/p&gt;

&lt;p&gt;module structure&lt;br&gt;
XML&lt;br&gt;
models&lt;br&gt;
views&lt;br&gt;
common patterns&lt;/p&gt;

&lt;p&gt;By the second month, I was moving beyond basic XML customization.&lt;/p&gt;

&lt;p&gt;I was able to work with Odoo OWL and build more custom controls.&lt;/p&gt;

&lt;p&gt;That mattered because it marked a transition.&lt;/p&gt;

&lt;p&gt;At the beginning, I was mostly learning how Odoo wanted things done.&lt;/p&gt;

&lt;p&gt;Later, I could start bending the framework toward actual requirements.&lt;/p&gt;

&lt;p&gt;That is a much better definition of progress than counting tutorial hours.&lt;/p&gt;

&lt;p&gt;The Goal Was Never to Become an Expert in 30 Days&lt;/p&gt;

&lt;p&gt;This is where these stories often become misleading.&lt;/p&gt;

&lt;p&gt;I did not become an Odoo expert in one month.&lt;/p&gt;

&lt;p&gt;That is not the lesson.&lt;/p&gt;

&lt;p&gt;I became useful enough to prove that I could do the work.&lt;/p&gt;

&lt;p&gt;Those are very different claims.&lt;/p&gt;

&lt;p&gt;Being useful means you can:&lt;/p&gt;

&lt;p&gt;understand the basic structure&lt;br&gt;
navigate the framework&lt;br&gt;
build something real&lt;br&gt;
diagnose common failures&lt;br&gt;
research unfamiliar problems&lt;br&gt;
make progress without constant supervision&lt;/p&gt;

&lt;p&gt;Expertise takes much longer.&lt;/p&gt;

&lt;p&gt;That is much closer to how I think developers should approach unfamiliar technology.&lt;/p&gt;

&lt;p&gt;Do not ask:&lt;/p&gt;

&lt;p&gt;How do I master this?&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;p&gt;What would make me useful with this?&lt;/p&gt;

&lt;p&gt;How I Would Learn an Unfamiliar Technology Today&lt;/p&gt;

&lt;p&gt;If I had to repeat the same experience with another framework, platform, or programming language, I would use almost the same process.&lt;/p&gt;

&lt;p&gt;Day 1 to 3: Understand the Shape&lt;/p&gt;

&lt;p&gt;Do not go deep.&lt;/p&gt;

&lt;p&gt;Figure out:&lt;/p&gt;

&lt;p&gt;project structure&lt;br&gt;
entry points&lt;br&gt;
configuration&lt;br&gt;
data model&lt;br&gt;
common terminology&lt;br&gt;
how the smallest working example looks&lt;/p&gt;

&lt;p&gt;Your goal is orientation.&lt;/p&gt;

&lt;p&gt;Day 4 to 7: Read Tiny Real Projects&lt;/p&gt;

&lt;p&gt;Find small repositories.&lt;/p&gt;

&lt;p&gt;Avoid huge production codebases at first.&lt;/p&gt;

&lt;p&gt;Look for repetition.&lt;/p&gt;

&lt;p&gt;What files always appear?&lt;/p&gt;

&lt;p&gt;What patterns repeat?&lt;/p&gt;

&lt;p&gt;Where does logic live?&lt;/p&gt;

&lt;p&gt;Where does configuration live?&lt;/p&gt;

&lt;p&gt;Week 2: Rebuild Examples&lt;/p&gt;

&lt;p&gt;Stop reading so much.&lt;/p&gt;

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

&lt;p&gt;Recreate small features without constantly copying.&lt;/p&gt;

&lt;p&gt;When you forget something, look it up.&lt;/p&gt;

&lt;p&gt;Then continue.&lt;/p&gt;

&lt;p&gt;Week 3: Modify Aggressively&lt;/p&gt;

&lt;p&gt;Take things that work and change them.&lt;/p&gt;

&lt;p&gt;Add fields.&lt;/p&gt;

&lt;p&gt;Remove features.&lt;/p&gt;

&lt;p&gt;Change behaviors.&lt;/p&gt;

&lt;p&gt;Swap components.&lt;/p&gt;

&lt;p&gt;Push the framework outside the exact tutorial scenario.&lt;/p&gt;

&lt;p&gt;Week 4: Build One Real Project&lt;/p&gt;

&lt;p&gt;Pick something with clear requirements.&lt;/p&gt;

&lt;p&gt;Not a demo.&lt;/p&gt;

&lt;p&gt;Not “hello world.”&lt;/p&gt;

&lt;p&gt;Something that forces several concepts to work together.&lt;/p&gt;

&lt;p&gt;That project is where your fragmented knowledge starts becoming a system.&lt;/p&gt;

&lt;p&gt;Do Not Confuse Learning With Content Consumption&lt;/p&gt;

&lt;p&gt;One of the biggest traps in software development is feeling productive because you are consuming technical material.&lt;/p&gt;

&lt;p&gt;Ten hours of video can feel substantial.&lt;/p&gt;

&lt;p&gt;But ask yourself:&lt;/p&gt;

&lt;p&gt;Could you build something without the instructor?&lt;/p&gt;

&lt;p&gt;Could you explain why the project is structured that way?&lt;/p&gt;

&lt;p&gt;Could you diagnose it when it breaks?&lt;/p&gt;

&lt;p&gt;Could you change the requirement?&lt;/p&gt;

&lt;p&gt;If not, you are probably still at the recognition stage.&lt;/p&gt;

&lt;p&gt;Recognition feels like understanding because the code looks familiar.&lt;/p&gt;

&lt;p&gt;But producing it independently is different.&lt;/p&gt;

&lt;p&gt;This is why I prefer a build-heavy learning method.&lt;/p&gt;

&lt;p&gt;The code exposes what you actually know.&lt;/p&gt;

&lt;p&gt;Read Existing Code Earlier&lt;/p&gt;

&lt;p&gt;This is probably the biggest habit I would recommend.&lt;/p&gt;

&lt;p&gt;A lot of developers delay reading other people's code because it feels intimidating.&lt;/p&gt;

&lt;p&gt;I think that is backwards.&lt;/p&gt;

&lt;p&gt;Existing repositories show you things that tutorials often hide:&lt;/p&gt;

&lt;p&gt;real naming conventions&lt;br&gt;
real file structure&lt;br&gt;
repetition&lt;br&gt;
workarounds&lt;br&gt;
patterns&lt;br&gt;
configuration&lt;br&gt;
boring glue code&lt;/p&gt;

&lt;p&gt;That boring code is often exactly what you need to become productive.&lt;/p&gt;

&lt;p&gt;Framework fluency comes from recognizing its patterns repeatedly.&lt;/p&gt;

&lt;p&gt;What About AI?&lt;/p&gt;

&lt;p&gt;My original Odoo transition happened before AI coding assistants became as deeply integrated into everyday development as they are now.&lt;/p&gt;

&lt;p&gt;Today, learning the same framework would probably be faster.&lt;/p&gt;

&lt;p&gt;But there is also a new risk.&lt;/p&gt;

&lt;p&gt;AI can remove the struggle that produces understanding.&lt;/p&gt;

&lt;p&gt;If every error goes directly into an AI assistant and you paste back the fix, you may ship faster while learning less.&lt;/p&gt;

&lt;p&gt;I would use AI today, but I would keep one boundary:&lt;/p&gt;

&lt;p&gt;Never let it remove the need to understand the change you are making.&lt;/p&gt;

&lt;p&gt;Ask it to explain.&lt;/p&gt;

&lt;p&gt;Ask it to compare approaches.&lt;/p&gt;

&lt;p&gt;Ask it where to look.&lt;/p&gt;

&lt;p&gt;Ask it why something failed.&lt;/p&gt;

&lt;p&gt;But if you cannot explain the final solution yourself, you have probably outsourced too much of the learning.&lt;/p&gt;

&lt;p&gt;A Better Way to Think About Technology Switching&lt;/p&gt;

&lt;p&gt;Developers often identify themselves too tightly with one stack.&lt;/p&gt;

&lt;p&gt;“I am a .NET developer.”&lt;/p&gt;

&lt;p&gt;“I am a Java developer.”&lt;/p&gt;

&lt;p&gt;“I am a React developer.”&lt;/p&gt;

&lt;p&gt;There is nothing wrong with specialization.&lt;/p&gt;

&lt;p&gt;I still have deep experience with .NET.&lt;/p&gt;

&lt;p&gt;But your programming language is not your entire professional identity.&lt;/p&gt;

&lt;p&gt;The more experienced you become, the more your value comes from things that survive stack changes:&lt;/p&gt;

&lt;p&gt;debugging&lt;br&gt;
decomposition&lt;br&gt;
architecture&lt;br&gt;
data modeling&lt;br&gt;
reasoning&lt;br&gt;
communication&lt;br&gt;
requirement analysis&lt;br&gt;
tradeoffs&lt;br&gt;
maintainability&lt;br&gt;
learning speed&lt;/p&gt;

&lt;p&gt;That is why experienced developers can sometimes transition faster than they expect.&lt;/p&gt;

&lt;p&gt;The syntax changes.&lt;/p&gt;

&lt;p&gt;The underlying problems are often familiar.&lt;/p&gt;

&lt;p&gt;Should You Apply for Jobs Where You Do Not Match Every Requirement?&lt;/p&gt;

&lt;p&gt;Sometimes, yes.&lt;/p&gt;

&lt;p&gt;But there is a difference between being underqualified and being dishonest.&lt;/p&gt;

&lt;p&gt;I would still apply when:&lt;/p&gt;

&lt;p&gt;I understand the underlying engineering domain&lt;br&gt;
the missing skill is learnable&lt;br&gt;
I have evidence of learning similar things before&lt;br&gt;
I can articulate how my existing skills transfer&lt;br&gt;
I am willing to prove the claim&lt;/p&gt;

&lt;p&gt;I would not claim experience I do not have.&lt;/p&gt;

&lt;p&gt;That distinction probably helped me more than pretending would have.&lt;/p&gt;

&lt;p&gt;I was not selling Odoo expertise.&lt;/p&gt;

&lt;p&gt;I was selling adaptability.&lt;/p&gt;

&lt;p&gt;And then I had to pay up by actually learning it.&lt;/p&gt;

&lt;p&gt;The Repeatable Learning System&lt;/p&gt;

&lt;p&gt;If I reduce the entire experience to one system, it is this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Find the smallest real structure&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inspect existing examples&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Memorize enough syntax to remove friction&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rebuild examples yourself&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modify them&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Break them&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Debug them&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build one real project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep learning after you become employable&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the part I still use.&lt;/p&gt;

&lt;p&gt;Not Odoo specifically.&lt;/p&gt;

&lt;p&gt;The learning process.&lt;/p&gt;

&lt;p&gt;I Turned This Experience Into a Practical Guide&lt;/p&gt;

&lt;p&gt;I recently turned the full experience into a short practical PDF:&lt;/p&gt;

&lt;p&gt;I Got an Odoo Job With Zero Odoo Experience&lt;/p&gt;

&lt;p&gt;It includes the learning system, the 30-day structure, how I approached unfamiliar code, how I used existing programming experience, what happened after I got the role, and how to adapt the same process to another technology.&lt;/p&gt;

&lt;p&gt;You can find it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://yaman95.gumroad.com/l/i-got-odoo-job-with-zero-odoo-experience" rel="noopener noreferrer"&gt;https://yaman95.gumroad.com/l/i-got-odoo-job-with-zero-odoo-experience&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The guide is not about promising that someone can become an expert in 30 days.&lt;/p&gt;

&lt;p&gt;It is about becoming useful quickly enough to start doing real work, then continuing to learn from the work itself.&lt;/p&gt;

&lt;p&gt;About Ayman Atif&lt;/p&gt;

&lt;p&gt;I am Ayman Atif, a software engineer from Morocco with more than 10 years of experience building business software.&lt;/p&gt;

&lt;p&gt;I have worked primarily with .NET and C#, alongside Python, Flask, Django, PostgreSQL, REST APIs, automation, CRM systems, financial applications, Android development, and production business platforms.&lt;/p&gt;

&lt;p&gt;My work focuses heavily on understanding business problems, designing practical software around them, improving existing systems, and maintaining applications after they reach production.&lt;/p&gt;

&lt;p&gt;You can see my portfolio, selected projects, services, and other technical writing here:&lt;/p&gt;

&lt;p&gt;Ayman Atif, Software Engineer&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;https://ayman-atif.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are learning a new technology, switching stacks, or trying to become productive in an unfamiliar framework, I hope this experience gives you a useful way to approach it.&lt;/p&gt;

&lt;p&gt;Do not wait until everything feels familiar.&lt;/p&gt;

&lt;p&gt;Familiarity often comes after you start building.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>odoo</category>
      <category>career</category>
      <category>hiring</category>
    </item>
    <item>
      <title>10 Years of Building Business Software: What Production Systems Taught Me That Tutorials Didn’t</title>
      <dc:creator>Ayman Atif</dc:creator>
      <pubDate>Sat, 12 Sep 2026 13:53:06 +0000</pubDate>
      <link>https://dev.to/a95yman/10-years-of-building-business-software-what-production-systems-taught-me-that-tutorials-didnt-5hka</link>
      <guid>https://dev.to/a95yman/10-years-of-building-business-software-what-production-systems-taught-me-that-tutorials-didnt-5hka</guid>
      <description>&lt;p&gt;I started developing software in 2014.&lt;/p&gt;

&lt;p&gt;Back then, like most developers, I spent a lot of time thinking about code.&lt;/p&gt;

&lt;p&gt;Was this the right design pattern? Was there a cleaner way to write this method? Should this logic live in another class? Was I using the framework correctly?&lt;/p&gt;

&lt;p&gt;Those questions matter. I still ask them.&lt;/p&gt;

&lt;p&gt;But after more than 10 years of building software, I have learned that they are rarely the hardest questions.&lt;/p&gt;

&lt;p&gt;The difficult part starts when software leaves your development environment and becomes something a business actually depends on.&lt;/p&gt;

&lt;p&gt;A feature can work perfectly and still be the wrong feature.&lt;/p&gt;

&lt;p&gt;A database query can be fine with 500 rows and become a problem with 500,000.&lt;/p&gt;

&lt;p&gt;A workflow that makes perfect sense to a developer can frustrate the people who have to use it every day.&lt;/p&gt;

&lt;p&gt;And a system that looks well designed today can become surprisingly difficult to change two years from now.&lt;/p&gt;

&lt;p&gt;Those are lessons that are difficult to learn from tutorials.&lt;/p&gt;

&lt;p&gt;You learn them by building, maintaining, breaking, fixing and improving real software.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Business Problem Comes Before the Technology
&lt;/h2&gt;

&lt;p&gt;Earlier in my career, I was much more likely to start thinking about implementation as soon as someone described a feature.&lt;/p&gt;

&lt;p&gt;Today, I try to understand the process first.&lt;/p&gt;

&lt;p&gt;What is the person actually trying to accomplish?&lt;/p&gt;

&lt;p&gt;What are they doing manually today?&lt;/p&gt;

&lt;p&gt;Where does the information come from?&lt;/p&gt;

&lt;p&gt;Who is responsible for it?&lt;/p&gt;

&lt;p&gt;What happens when something goes wrong?&lt;/p&gt;

&lt;p&gt;What part of the process is actually expensive or frustrating?&lt;/p&gt;

&lt;p&gt;Sometimes a client asks for a feature, but after following the workflow through, you realize that the requested feature is solving the symptom rather than the underlying problem.&lt;/p&gt;

&lt;p&gt;That changes the equation.&lt;/p&gt;

&lt;p&gt;The job is no longer simply:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build what was requested.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Understand why it was requested, then determine what should actually be built.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is especially important with business software because existing processes often contain years of knowledge.&lt;/p&gt;

&lt;p&gt;A spreadsheet, for example, might look messy from an engineering perspective. But hidden inside that spreadsheet can be calculations, exceptions and business rules that employees have developed over years.&lt;/p&gt;

&lt;p&gt;Replacing it with software without understanding those rules can make the new system worse than the spreadsheet it replaced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Changes Your Definition of "Working"
&lt;/h2&gt;

&lt;p&gt;During development, it is easy to define success like this:&lt;/p&gt;

&lt;p&gt;The feature works.&lt;/p&gt;

&lt;p&gt;In production, that definition is nowhere near enough.&lt;/p&gt;

&lt;p&gt;Does it work when several people use it at the same time?&lt;/p&gt;

&lt;p&gt;Does it still work when the database becomes much larger?&lt;/p&gt;

&lt;p&gt;What happens when an external service fails?&lt;/p&gt;

&lt;p&gt;What happens when someone uploads unexpected data?&lt;/p&gt;

&lt;p&gt;Can you understand what happened when something breaks?&lt;/p&gt;

&lt;p&gt;Can another developer safely change the code six months later?&lt;/p&gt;

&lt;p&gt;Can the business recover if something goes wrong?&lt;/p&gt;

&lt;p&gt;A production system has responsibilities that a demo does not.&lt;/p&gt;

&lt;p&gt;I have worked on business applications involving financial data, document generation, reporting, CRM workflows, automation, APIs and complex database operations.&lt;/p&gt;

&lt;p&gt;The bugs that take the most time are often not obvious programming mistakes.&lt;/p&gt;

&lt;p&gt;They appear because several individually reasonable decisions interact in an unexpected way.&lt;/p&gt;

&lt;p&gt;That is one reason I have become much more interested in system behavior than isolated pieces of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance Problems Are Often System Problems
&lt;/h2&gt;

&lt;p&gt;When an application becomes slow, the tempting response is to look for slow code.&lt;/p&gt;

&lt;p&gt;Sometimes that is exactly the problem.&lt;/p&gt;

&lt;p&gt;Often it isn't.&lt;/p&gt;

&lt;p&gt;The slowdown might involve database queries, indexes, repeated API calls, unnecessary browser work, server memory, large documents, network latency or several of those things compounding each other.&lt;/p&gt;

&lt;p&gt;Optimizing one function might accomplish almost nothing.&lt;/p&gt;

&lt;p&gt;I learned to trace the entire path instead.&lt;/p&gt;

&lt;p&gt;What happens from the moment the user clicks the button until the result appears?&lt;/p&gt;

&lt;p&gt;Where is time actually being spent?&lt;/p&gt;

&lt;p&gt;What data is being transferred?&lt;/p&gt;

&lt;p&gt;What work is repeated?&lt;/p&gt;

&lt;p&gt;What resources are constrained?&lt;/p&gt;

&lt;p&gt;You have to follow the argument through instead of assuming that the most visible part of the application is responsible.&lt;/p&gt;

&lt;p&gt;This mindset has saved me from "optimizing" code that wasn't causing the problem in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Existing Software Deserves Investigation Before a Rewrite
&lt;/h2&gt;

&lt;p&gt;Developers love clean starts.&lt;/p&gt;

&lt;p&gt;There is something satisfying about looking at an old application and imagining how much better it would be if we rebuilt everything using a modern architecture.&lt;/p&gt;

&lt;p&gt;Sometimes a rewrite is justified.&lt;/p&gt;

&lt;p&gt;But I have become much more cautious about recommending one.&lt;/p&gt;

&lt;p&gt;Old software contains knowledge.&lt;/p&gt;

&lt;p&gt;It contains strange conditions because somebody encountered a strange situation five years ago.&lt;/p&gt;

&lt;p&gt;It contains validation rules because something once went wrong.&lt;/p&gt;

&lt;p&gt;It contains workflows that employees have quietly adapted to.&lt;/p&gt;

&lt;p&gt;Some of the code may genuinely be bad. But deleting code is much easier than recovering the business knowledge that was encoded inside it.&lt;/p&gt;

&lt;p&gt;When I inherit an existing application, I prefer to understand it first.&lt;/p&gt;

&lt;p&gt;Find the fragile areas.&lt;/p&gt;

&lt;p&gt;Identify the dependencies.&lt;/p&gt;

&lt;p&gt;Understand the data.&lt;/p&gt;

&lt;p&gt;Fix the highest-risk problems.&lt;/p&gt;

&lt;p&gt;Improve boundaries gradually.&lt;/p&gt;

&lt;p&gt;Then decide whether replacing larger parts of the system is actually worth it.&lt;/p&gt;

&lt;p&gt;A rewrite should be an engineering decision, not an emotional reaction to ugly code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation Is Mostly About Understanding Repetition
&lt;/h2&gt;

&lt;p&gt;Automation is another area where the programming is often the easy part.&lt;/p&gt;

&lt;p&gt;The real work is understanding the process well enough to automate it safely.&lt;/p&gt;

&lt;p&gt;Imagine someone repeatedly:&lt;/p&gt;

&lt;p&gt;opens a spreadsheet,&lt;/p&gt;

&lt;p&gt;copies information from another system,&lt;/p&gt;

&lt;p&gt;checks several conditions,&lt;/p&gt;

&lt;p&gt;generates a document,&lt;/p&gt;

&lt;p&gt;sends an email,&lt;/p&gt;

&lt;p&gt;and updates a status somewhere else.&lt;/p&gt;

&lt;p&gt;You could automate those steps.&lt;/p&gt;

&lt;p&gt;But first you need to know why each step exists.&lt;/p&gt;

&lt;p&gt;Which decisions require human judgment?&lt;/p&gt;

&lt;p&gt;Which rules are consistent?&lt;/p&gt;

&lt;p&gt;What happens when information is missing?&lt;/p&gt;

&lt;p&gt;What should happen when something fails halfway through?&lt;/p&gt;

&lt;p&gt;What needs to be logged?&lt;/p&gt;

&lt;p&gt;Can the process be resumed?&lt;/p&gt;

&lt;p&gt;Good automation does not simply make something faster.&lt;/p&gt;

&lt;p&gt;It makes a process more structured, repeatable and easier to inspect.&lt;/p&gt;

&lt;p&gt;That distinction has shaped the way I approach business process automation today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintainability Becomes Very Real When You Maintain Your Own Code
&lt;/h2&gt;

&lt;p&gt;There is a particular kind of humility that comes from opening code you wrote two years ago.&lt;/p&gt;

&lt;p&gt;At the time, everything seemed obvious.&lt;/p&gt;

&lt;p&gt;Two years later, you might stare at it wondering why anyone would structure something that way.&lt;/p&gt;

&lt;p&gt;Then you check the Git history.&lt;/p&gt;

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

&lt;p&gt;It was you.&lt;/p&gt;

&lt;p&gt;That experience changes how you think about maintainability.&lt;/p&gt;

&lt;p&gt;Clean code is not about making code look clever.&lt;/p&gt;

&lt;p&gt;Architecture is not about using as many abstractions as possible.&lt;/p&gt;

&lt;p&gt;Patterns are not achievements by themselves.&lt;/p&gt;

&lt;p&gt;The purpose is to make future changes safer and easier to reason about.&lt;/p&gt;

&lt;p&gt;Sometimes an abstraction helps.&lt;/p&gt;

&lt;p&gt;Sometimes three straightforward functions are better than an elaborate hierarchy.&lt;/p&gt;

&lt;p&gt;Sometimes duplication is temporarily less dangerous than coupling two things that only happen to look similar today.&lt;/p&gt;

&lt;p&gt;The longer I work in software, the less interested I become in rules that say you should "always" structure code a particular way.&lt;/p&gt;

&lt;p&gt;Context matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple Software Is Usually Hard to Build
&lt;/h2&gt;

&lt;p&gt;One of the most counterintuitive lessons I have learned is that simple software can require a lot of engineering.&lt;/p&gt;

&lt;p&gt;Users should not need to understand your database structure.&lt;/p&gt;

&lt;p&gt;They should not need to know that three APIs are involved.&lt;/p&gt;

&lt;p&gt;They should not care that a document is assembled from multiple sources.&lt;/p&gt;

&lt;p&gt;They want to click something and have the correct result appear.&lt;/p&gt;

&lt;p&gt;Making that experience simple often means handling considerable complexity behind the scenes.&lt;/p&gt;

&lt;p&gt;The temptation is to cut corners and expose some of that complexity to the user.&lt;/p&gt;

&lt;p&gt;But every technical responsibility you push onto users makes the system harder for the business to operate.&lt;/p&gt;

&lt;p&gt;Good business software absorbs complexity instead of distributing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication Is an Engineering Skill
&lt;/h2&gt;

&lt;p&gt;Ten years ago, if you had asked me what makes someone a strong software developer, I probably would have based my answer mostly on technical ability.&lt;/p&gt;

&lt;p&gt;Today, I would revise my position.&lt;/p&gt;

&lt;p&gt;Technical skill still matters enormously, but professional software development also requires being able to articulate tradeoffs.&lt;/p&gt;

&lt;p&gt;Why are we building this?&lt;/p&gt;

&lt;p&gt;Why this approach?&lt;/p&gt;

&lt;p&gt;What happens if we don't fix this problem?&lt;/p&gt;

&lt;p&gt;Why will this take longer than expected?&lt;/p&gt;

&lt;p&gt;What are we giving up by choosing option A instead of option B?&lt;/p&gt;

&lt;p&gt;Engineers constantly weigh speed against maintainability, flexibility against complexity, and short-term requirements against long-term consequences.&lt;/p&gt;

&lt;p&gt;If those tradeoffs remain inside the developer's head, the business cannot participate in the decision.&lt;/p&gt;

&lt;p&gt;Being able to explain them clearly is part of the engineering work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Best Technical Decision Depends on the Business
&lt;/h2&gt;

&lt;p&gt;I work primarily with .NET and Python today.&lt;/p&gt;

&lt;p&gt;Both are powerful ecosystems, but I don't believe the technology should take precedence over the problem.&lt;/p&gt;

&lt;p&gt;Sometimes ASP.NET Core is an excellent foundation for a long-lived business application.&lt;/p&gt;

&lt;p&gt;Sometimes Python and Flask let you build a focused internal system quickly and maintain it comfortably.&lt;/p&gt;

&lt;p&gt;Sometimes the correct solution is not a new application at all.&lt;/p&gt;

&lt;p&gt;Maybe an existing process can be improved.&lt;/p&gt;

&lt;p&gt;Maybe a small automation is enough.&lt;/p&gt;

&lt;p&gt;Maybe the spreadsheet should stay, but the repetitive work around it should disappear.&lt;/p&gt;

&lt;p&gt;Good engineering is not about finding opportunities to use your favorite technology.&lt;/p&gt;

&lt;p&gt;It is about choosing an appropriate amount of technology for the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Care About After 10+ Years
&lt;/h2&gt;

&lt;p&gt;My definition of software engineering has changed considerably since I started in 2014.&lt;/p&gt;

&lt;p&gt;I still care about code quality.&lt;/p&gt;

&lt;p&gt;I still enjoy learning frameworks, improving architecture and solving difficult technical problems.&lt;/p&gt;

&lt;p&gt;But those things now sit inside a larger picture.&lt;/p&gt;

&lt;p&gt;I care about understanding why the software exists.&lt;/p&gt;

&lt;p&gt;I care about what happens when it reaches production.&lt;/p&gt;

&lt;p&gt;I care about whether someone can safely maintain it.&lt;/p&gt;

&lt;p&gt;I care about whether it remains useful when the business changes.&lt;/p&gt;

&lt;p&gt;And I care about whether the technology is actually making someone's work easier.&lt;/p&gt;

&lt;p&gt;That is the side of software engineering I find most interesting today.&lt;/p&gt;

&lt;p&gt;I write more about these lessons in my &lt;strong&gt;software engineering blog&lt;/strong&gt;, where I cover .NET, backend engineering, architecture decisions, production constraints, technical interviews and lessons from building real systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read my software engineering blog:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://ayman-atif.vercel.app/blog/" rel="noopener noreferrer"&gt;https://ayman-atif.vercel.app/blog/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're dealing with a business process that still relies heavily on spreadsheets, an application that has become difficult to maintain, or an idea that needs a technical plan, you can also learn more about my work here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ayman Atif — Software Engineer&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://ayman-atif.vercel.app/" rel="noopener noreferrer"&gt;https://ayman-atif.vercel.app/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaredevelopment</category>
      <category>programming</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
