<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Built for Production]]></title><description><![CDATA[Built for Production is a technical blog focused on practical software engineering beyond syntax. I write about .NET, Azure, architecture, APIs, AI-assisted dev]]></description><link>https://blog.devni.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Built for Production</title><link>https://blog.devni.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 10 Sep 2026 13:41:27 GMT</lastBuildDate><atom:link href="https://blog.devni.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Architecture Is Not Just Folders, Layers, or Diagrams]]></title><description><![CDATA[Architecture is not just about folders, layers, or diagrams.
Those things can help.
A clean folder structure can make a project easier to navigate. Layers can help separate responsibilities. Diagrams ]]></description><link>https://blog.devni.dev/architecture-is-not-just-folders-layers-or-diagrams</link><guid isPermaLink="true">https://blog.devni.dev/architecture-is-not-just-folders-layers-or-diagrams</guid><category><![CDATA[dotnet]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[System Design]]></category><category><![CDATA[Backend Development]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Devni Heraliyawala]]></dc:creator><pubDate>Sun, 21 Jun 2026 12:01:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a28fc1c0d3661f679c2132f/f1af9f59-0616-422d-8fd4-15cfcf8280bc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Architecture is not just about folders, layers, or diagrams.</p>
<p>Those things can help.</p>
<p>A clean folder structure can make a project easier to navigate. Layers can help separate responsibilities. Diagrams can help teams understand how different parts of a system connect.</p>
<p>But architecture is not the folder structure itself.</p>
<p>Architecture is the set of decisions that shape how a system behaves over time.</p>
<p>In backend systems, architecture shows up in how the application handles change, failure, complexity, dependencies, testing, and long-term maintenance.</p>
<p>A project can have perfect folder names and still be difficult to maintain.</p>
<p>A system can follow a popular pattern and still be overengineered.</p>
<p>Good architecture is not about making the code look sophisticated.</p>
<p>It is about making the system easier to change, test, operate, and reason about.</p>
<h2>Folder structure is not architecture</h2>
<p>Many backend projects start with folders like:</p>
<ul>
<li><p>Controllers</p>
</li>
<li><p>Services</p>
</li>
<li><p>Repositories</p>
</li>
<li><p>Models</p>
</li>
<li><p>DTOs</p>
</li>
<li><p>Helpers</p>
</li>
<li><p>Interfaces</p>
</li>
</ul>
<p>At first, this can look clean.</p>
<p>But folder names alone do not guarantee good design.</p>
<p>A project can have a <code>Services</code> folder where every business rule, database call, validation check, and integration call is mixed together.</p>
<p>A project can have a <code>Repositories</code> folder but still leak database concerns everywhere.</p>
<p>A project can have a <code>Domain</code> folder but still keep the actual business logic inside controllers.</p>
<p>A project can have <code>CleanArchitecture</code> in the repository name but still be hard to test, hard to change, and hard to understand.</p>
<p>Architecture is not proven by the folder names.</p>
<p>It is proven by how responsibilities are separated and how easily the system can evolve.</p>
<h2>Architecture is about decisions</h2>
<p>In backend systems, architecture appears in decisions such as:</p>
<ul>
<li><p>Where should business rules live?</p>
</li>
<li><p>How should modules communicate?</p>
</li>
<li><p>What should be synchronous vs asynchronous?</p>
</li>
<li><p>When should data be duplicated?</p>
</li>
<li><p>How should failures be handled?</p>
</li>
<li><p>What happens when one dependency is slow or unavailable?</p>
</li>
<li><p>How easy is it to test the core logic?</p>
</li>
<li><p>How hard is it to change one feature without breaking another?</p>
</li>
<li><p>Can a new developer understand the system without fear?</p>
</li>
<li><p>Will this design still make sense six months from now?</p>
</li>
</ul>
<p>These questions matter more than whether the project has a perfect folder layout.</p>
<p>Good architecture is not only about structure.</p>
<p>It is about trade-offs.</p>
<h2>Business rules need a proper home</h2>
<p>One common architecture problem is business logic being placed in the wrong layer.</p>
<p>In ASP.NET Core applications, it is easy for controllers to become too powerful.</p>
<p>A controller starts by accepting a request and returning a response.</p>
<p>Then it adds validation.</p>
<p>Then it adds business rules.</p>
<p>Then it calls the database.</p>
<p>Then it calls an external API.</p>
<p>Then it handles exceptions.</p>
<p>Then it maps the response.</p>
<p>Before long, the controller becomes the center of the system.</p>
<p>That creates problems.</p>
<p>Controllers should usually handle HTTP concerns, not own the core business logic.</p>
<p>Business rules should live in a place where they can be tested, reused, and changed without being tightly coupled to the web framework.</p>
<p>Depending on the architecture, that may be in application services, domain services, command handlers, use case handlers, or domain models.</p>
<p>The exact pattern is less important than the principle:</p>
<p>Business logic should not be trapped inside the delivery mechanism.</p>
<h2>Module communication shapes complexity</h2>
<p>Another important architecture decision is how different parts of the system communicate.</p>
<p>In a simple application, direct method calls may be enough.</p>
<p>In a larger system, modules may need clearer boundaries.</p>
<p>For example, an order module may need to communicate with payment, inventory, notification, or reporting logic.</p>
<p>The question is not only:</p>
<p>“How do we call this code?”</p>
<p>The better question is:</p>
<p>“How tightly should these parts depend on each other?”</p>
<p>If every module knows too much about every other module, the system becomes fragile.</p>
<p>A small change in one area can unexpectedly break another area.</p>
<p>Good architecture reduces unnecessary coupling.</p>
<p>That does not always mean microservices.</p>
<p>Sometimes a modular monolith with clear internal boundaries is a better design than many small services with poor communication patterns.</p>
<p>Architecture is about choosing the right level of separation for the problem.</p>
<h2>Synchronous vs asynchronous decisions matter</h2>
<p>Not every operation needs to happen immediately.</p>
<p>Some operations should be synchronous because the user needs the result right away.</p>
<p>For example:</p>
<ul>
<li><p>validating a request</p>
</li>
<li><p>checking permissions</p>
</li>
<li><p>calculating a response</p>
</li>
<li><p>returning requested data</p>
</li>
</ul>
<p>Other operations may be better handled asynchronously.</p>
<p>For example:</p>
<ul>
<li><p>sending emails</p>
</li>
<li><p>generating reports</p>
</li>
<li><p>processing uploaded files</p>
</li>
<li><p>syncing with external systems</p>
</li>
<li><p>publishing events</p>
</li>
<li><p>running long background workflows</p>
</li>
</ul>
<p>If everything is synchronous, the API can become slow and fragile.</p>
<p>If everything is asynchronous, the system can become harder to reason about.</p>
<p>Good architecture decides where immediate consistency is required and where eventual consistency is acceptable.</p>
<p>This decision affects user experience, reliability, performance, and operational complexity.</p>
<h2>Failure handling is an architectural concern</h2>
<p>Failures are not rare edge cases.</p>
<p>They are part of real software.</p>
<p>Databases can be slow.</p>
<p>External APIs can fail.</p>
<p>Message queues can be delayed.</p>
<p>Files can be missing.</p>
<p>Authentication providers can be unavailable.</p>
<p>Background jobs can fail halfway.</p>
<p>A good architecture asks:</p>
<ul>
<li><p>What happens when this dependency fails?</p>
</li>
<li><p>Should this operation retry?</p>
</li>
<li><p>Is the operation idempotent?</p>
</li>
<li><p>Should the user see an error immediately?</p>
</li>
<li><p>Should the system continue and process later?</p>
</li>
<li><p>How will the failure be logged?</p>
</li>
<li><p>How will support teams investigate the issue?</p>
</li>
<li><p>Can the system recover safely?</p>
</li>
</ul>
<p>Failure handling should not be added randomly in each method.</p>
<p>It should be part of the system design.</p>
<p>A backend system that does not plan for failure will eventually make failure harder to debug, harder to recover from, and more expensive to support.</p>
<h2>Testability reveals architectural quality</h2>
<p>One of the best ways to evaluate architecture is to ask:</p>
<p>“How easy is this system to test?”</p>
<p>If testing core business logic requires a real database, a running web server, external services, configuration files, and many unrelated dependencies, the design may be too tightly coupled.</p>
<p>Good architecture makes important logic testable without unnecessary infrastructure.</p>
<p>That does not mean everything needs to be mocked.</p>
<p>It means the system should have clear boundaries.</p>
<p>For example:</p>
<ul>
<li><p>business rules should be testable without HTTP</p>
</li>
<li><p>validation rules should be testable without the database</p>
</li>
<li><p>application workflows should be testable without external APIs</p>
</li>
<li><p>infrastructure details should be replaceable where needed</p>
</li>
<li><p>side effects should be controlled and intentional</p>
</li>
</ul>
<p>Testability is not only about writing tests.</p>
<p>It is also feedback about design quality.</p>
<p>A system that is hard to test is often also hard to change.</p>
<h2>Popular patterns can still be overengineered</h2>
<p>Clean Architecture, CQRS, DDD, microservices, event-driven architecture, and vertical slice architecture can all be useful.</p>
<p>But no pattern is automatically good.</p>
<p>A small CRUD application may not need complex domain modelling, message brokers, multiple projects, and many layers of abstraction.</p>
<p>A large enterprise workflow may need stronger boundaries, background processing, audit trails, domain rules, and clear separation between application and infrastructure concerns.</p>
<p>The mistake is not using patterns.</p>
<p>The mistake is using patterns without understanding the problem.</p>
<p>Overengineering often happens when architecture is treated as a checklist instead of a set of trade-offs.</p>
<p>Good architecture should reduce complexity, not decorate it.</p>
<h2>AI-assisted development needs architectural direction</h2>
<p>With AI-assisted development, architecture becomes even more important.</p>
<p>AI can generate controllers, services, repositories, DTOs, tests, and database queries quickly.</p>
<p>But generated code still needs direction.</p>
<p>It needs boundaries.</p>
<p>It needs consistency.</p>
<p>It needs human judgement.</p>
<p>Without architectural guidance, AI can easily generate code that works in isolation but does not fit the system.</p>
<p>For example, it may:</p>
<ul>
<li><p>place business logic in the wrong layer</p>
</li>
<li><p>duplicate logic that already exists</p>
</li>
<li><p>create inconsistent error handling</p>
</li>
<li><p>ignore existing validation patterns</p>
</li>
<li><p>bypass authorization rules</p>
</li>
<li><p>introduce unnecessary abstractions</p>
</li>
<li><p>return internal models from APIs</p>
</li>
<li><p>create code that is hard to test</p>
</li>
<li><p>miss logging and observability requirements</p>
</li>
</ul>
<p>AI can speed up implementation.</p>
<p>But architecture decides whether the implementation belongs in the system.</p>
<p>That is why strong architectural thinking becomes more valuable, not less valuable, in the age of AI.</p>
<h2>Enterprise systems live for years</h2>
<p>For .NET developers, architecture matters a lot because many enterprise systems live for years.</p>
<p>A backend system may start small, but over time it gains more features, more integrations, more users, more business rules, and more operational responsibility.</p>
<p>The design decisions made early can become the maintenance cost someone else pays later.</p>
<p>A shortcut today may become a production problem later.</p>
<p>A poorly placed business rule may become a hidden dependency.</p>
<p>An unclear module boundary may slow down future changes.</p>
<p>A missing logging strategy may make incidents harder to investigate.</p>
<p>An overcomplicated pattern may make onboarding harder for new developers.</p>
<p>Architecture is not only about the first version of the system.</p>
<p>It is about the system’s ability to survive change.</p>
<h2>Good architecture supports change</h2>
<p>Software changes because businesses change.</p>
<p>Requirements change.</p>
<p>Integrations change.</p>
<p>Security requirements change.</p>
<p>Data grows.</p>
<p>Teams grow.</p>
<p>Deployment environments change.</p>
<p>Users discover new needs.</p>
<p>Good architecture accepts that change will happen.</p>
<p>It tries to make change safer.</p>
<p>That does not mean predicting every future requirement.</p>
<p>It means designing with enough clarity that future changes can be made without fear.</p>
<p>A good architecture helps developers answer:</p>
<ul>
<li><p>Where should this change go?</p>
</li>
<li><p>What parts of the system will be affected?</p>
</li>
<li><p>What tests should be updated?</p>
</li>
<li><p>What dependencies are involved?</p>
</li>
<li><p>What risks should be considered?</p>
</li>
<li><p>How can this change be deployed safely?</p>
</li>
</ul>
<p>When a system has clear boundaries and predictable patterns, change becomes less dangerous.</p>
<h2>A practical architecture checklist</h2>
<p>Before deciding on an architecture, it helps to ask practical questions.</p>
<ul>
<li><p>Where do business rules live?</p>
</li>
<li><p>Are controllers or endpoints too powerful?</p>
</li>
<li><p>Are modules clearly separated?</p>
</li>
<li><p>Are dependencies flowing in the right direction?</p>
</li>
<li><p>Is the core logic testable?</p>
</li>
<li><p>Are infrastructure concerns isolated?</p>
</li>
<li><p>Is the design simple enough for the problem?</p>
</li>
<li><p>Are asynchronous workflows used only where they make sense?</p>
</li>
<li><p>Are failures handled intentionally?</p>
</li>
<li><p>Are logs, metrics, and traces considered?</p>
</li>
<li><p>Can a new developer understand the system?</p>
</li>
<li><p>Will this design still make sense six months from now?</p>
</li>
</ul>
<p>This checklist does not guarantee perfect architecture.</p>
<p>But it helps keep the focus on maintainability, changeability, reliability, and clarity.</p>
<h2>Final thoughts</h2>
<p>Architecture is not just about folders, layers, or diagrams.</p>
<p>Those things can help, but they are not the real goal.</p>
<p>Good architecture is about the decisions that shape how a system behaves over time.</p>
<p>It is about where business rules live.</p>
<p>How modules communicate.</p>
<p>How failures are handled.</p>
<p>How easy the system is to test.</p>
<p>How safely the system can change.</p>
<p>How clearly developers can reason about the code.</p>
<p>And how well the design supports real-world operation.</p>
<p>A system can look clean and still be difficult to maintain.</p>
<p>A system can follow a popular pattern and still be overengineered.</p>
<p>Good architecture is not about making code look sophisticated.</p>
<p>It is about making the system easier to change, test, operate, and reason about.</p>
<p>For .NET developers, this matters because many enterprise systems live for years.</p>
<p>The design decisions made today become the maintenance cost someone else pays tomorrow.</p>
<p>What architecture mistake have you seen create problems later in a project?</p>
]]></content:encoded></item><item><title><![CDATA[An API Endpoint Is Not the Same as API Design]]></title><description><![CDATA[Creating an API endpoint is easy.
Designing an API that other developers can trust is harder.
In ASP.NET Core, exposing an endpoint can be very straightforward:
GET /orders/{id}
POST /orders
PUT /orde]]></description><link>https://blog.devni.dev/an-api-endpoint-is-not-the-same-as-api-design</link><guid isPermaLink="true">https://blog.devni.dev/an-api-endpoint-is-not-the-same-as-api-design</guid><category><![CDATA[dotnet]]></category><category><![CDATA[asp.net core]]></category><category><![CDATA[API Design]]></category><category><![CDATA[Backend Development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[REST API]]></category><dc:creator><![CDATA[Devni Heraliyawala]]></dc:creator><pubDate>Mon, 15 Jun 2026 04:59:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a28fc1c0d3661f679c2132f/224892fd-c1ac-4da9-9821-fa6ae0560957.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Creating an API endpoint is easy.</p>
<p>Designing an API that other developers can trust is harder.</p>
<p>In ASP.NET Core, exposing an endpoint can be very straightforward:</p>
<p><code>GET /orders/{id}</code></p>
<p><code>POST /orders</code></p>
<p><code>PUT /orders/{id}</code></p>
<p><code>DELETE /orders/{id}</code></p>
<p>A controller action or minimal API route can be created quickly. The endpoint may accept a request, call a service, return a response, and look complete.</p>
<p>But an endpoint is not the same as API design.</p>
<p>Good API design starts before the controller action.</p>
<p>It starts with understanding the contract, the consumer, the business rules, the failure scenarios, the security boundaries, and the long-term maintainability of the system.</p>
<p>A clean endpoint with poor contracts can still create problems.</p>
<p>A good API should be predictable, secure, consistent, observable, and easy to consume.</p>
<h2>An endpoint is only the surface</h2>
<p>An endpoint is the visible part of the API.</p>
<p>It is the URL, HTTP method, request body, response body, and status code that another developer interacts with.</p>
<p>But behind that endpoint, there are many design decisions.</p>
<p>For example:</p>
<ul>
<li><p>What should the request contract look like?</p>
</li>
<li><p>What should the response contract expose?</p>
</li>
<li><p>Which fields are required?</p>
</li>
<li><p>How should validation errors be returned?</p>
</li>
<li><p>Which HTTP status codes should be used?</p>
</li>
<li><p>How should authentication and authorization be enforced?</p>
</li>
<li><p>Should the operation be idempotent?</p>
</li>
<li><p>How should pagination, filtering, and sorting work?</p>
</li>
<li><p>What happens when a downstream service fails?</p>
</li>
<li><p>How will the API remain backward compatible?</p>
</li>
<li><p>How will logs and traces help debug production issues?</p>
</li>
</ul>
<p>These questions are part of API design.</p>
<p>If they are ignored, the endpoint may still work, but it may become difficult to consume, difficult to maintain, and difficult to support in production.</p>
<h2>Request contracts should be intentional</h2>
<p>A request contract should not simply mirror the database table.</p>
<p>This is a common API design mistake.</p>
<p>The database model and the API contract serve different purposes.</p>
<p>A database model is designed for persistence.</p>
<p>An API request model is designed for communication.</p>
<p>For example, an order creation request should include only the data the client is allowed to send. It should not expose internal fields such as database IDs, audit fields, status transitions, calculated values, or system-managed properties.</p>
<p>A good request contract should be:</p>
<ul>
<li><p>clear</p>
</li>
<li><p>minimal</p>
</li>
<li><p>validatable</p>
</li>
<li><p>stable</p>
</li>
<li><p>aligned with the business operation</p>
</li>
</ul>
<p>For example, this is more intentional:</p>
<p><code>CreateOrderRequest</code></p>
<p>with fields such as:</p>
<ul>
<li><p>customerId</p>
</li>
<li><p>orderItems</p>
</li>
<li><p>deliveryAddress</p>
</li>
<li><p>requestedDeliveryDate</p>
</li>
</ul>
<p>Instead of exposing a full internal <code>Order</code> entity directly.</p>
<p>The request should represent the action the client is trying to perform, not the internal shape of the database.</p>
<h2>Response contracts should protect the system</h2>
<p>Response contracts are just as important as request contracts.</p>
<p>A common mistake is returning internal domain entities or database models directly from an API.</p>
<p>This can create several problems.</p>
<p>It may expose fields that should remain internal.</p>
<p>It may tightly couple clients to the database structure.</p>
<p>It may make future changes harder.</p>
<p>It may accidentally leak sensitive or unnecessary information.</p>
<p>A response contract should expose what the consumer needs, not everything the system knows.</p>
<p>For example, an order response may include:</p>
<ul>
<li><p>orderId</p>
</li>
<li><p>orderNumber</p>
</li>
<li><p>status</p>
</li>
<li><p>createdAt</p>
</li>
<li><p>totalAmount</p>
</li>
<li><p>items</p>
</li>
</ul>
<p>But it may not need to expose:</p>
<ul>
<li><p>internal workflow flags</p>
</li>
<li><p>database tracking fields</p>
</li>
<li><p>audit metadata</p>
</li>
<li><p>internal notes</p>
</li>
<li><p>security-related fields</p>
</li>
<li><p>implementation-specific values</p>
</li>
</ul>
<p>Good response contracts create a boundary between the internal system and external consumers.</p>
<p>That boundary helps the API evolve safely.</p>
<h2>Validation should be predictable</h2>
<p>Validation is not only about rejecting bad input.</p>
<p>It is also about helping the consumer understand what went wrong.</p>
<p>A poor validation response might return a generic message like:</p>
<p><code>Invalid request</code></p>
<p>That does not help the client fix the problem.</p>
<p>A better API returns structured validation errors that clearly explain which fields failed and why.</p>
<p>For example:</p>
<ul>
<li><p><code>customerId is required</code></p>
</li>
<li><p><code>orderItems must contain at least one item</code></p>
</li>
<li><p><code>requestedDeliveryDate cannot be in the past</code></p>
</li>
</ul>
<p>In ASP.NET Core, validation can be handled through model validation, FluentValidation, filters, middleware, or endpoint-specific logic depending on the architecture.</p>
<p>The important part is consistency.</p>
<p>Every endpoint should not invent its own validation response format.</p>
<p>A predictable validation format makes the API easier to integrate with and easier to debug.</p>
<h2>Status codes are part of the contract</h2>
<p>HTTP status codes are not just technical details.</p>
<p>They are part of the API contract.</p>
<p>A well-designed API uses status codes consistently.</p>
<p>For example:</p>
<ul>
<li><p><code>200 OK</code> for successful reads or updates where a response body is returned</p>
</li>
<li><p><code>201 Created</code> when a new resource is created</p>
</li>
<li><p><code>204 No Content</code> when an operation succeeds without returning content</p>
</li>
<li><p><code>400 Bad Request</code> for malformed or invalid requests</p>
</li>
<li><p><code>401 Unauthorized</code> when authentication is missing or invalid</p>
</li>
<li><p><code>403 Forbidden</code> when the user is authenticated but not allowed</p>
</li>
<li><p><code>404 Not Found</code> when the resource does not exist</p>
</li>
<li><p><code>409 Conflict</code> when the request conflicts with current system state</p>
</li>
<li><p><code>500 Internal Server Error</code> for unexpected server-side failures</p>
</li>
</ul>
<p>A common mistake is returning <code>200 OK</code> for everything and putting success or failure inside the response body.</p>
<p>That makes the API harder to consume correctly.</p>
<p>Clients should be able to rely on HTTP semantics.</p>
<p>The status code should tell the client what happened at a high level, and the response body should provide useful details when needed.</p>
<h2>Authentication and authorization are not the same</h2>
<p>Authentication answers the question:</p>
<p>“Who are you?”</p>
<p>Authorization answers the question:</p>
<p>“What are you allowed to do?”</p>
<p>An API may correctly authenticate a user but still fail to enforce proper authorization.</p>
<p>For example, a user may be logged in and allowed to view orders, but that does not mean they should be allowed to view every order in the system.</p>
<p>API design should consider authorization at the resource and operation level.</p>
<p>Questions to ask include:</p>
<ul>
<li><p>Can this user access this resource?</p>
</li>
<li><p>Can this user perform this action?</p>
</li>
<li><p>Is access based on role, ownership, tenant, permission, or policy?</p>
</li>
<li><p>Should authorization happen at the endpoint, service, or domain level?</p>
</li>
<li><p>Are there multi-tenant boundaries?</p>
</li>
<li><p>Are sensitive fields protected?</p>
</li>
</ul>
<p>In ASP.NET Core, policies, roles, claims, and custom authorization handlers can help enforce these rules.</p>
<p>But the design decision comes first.</p>
<p>Security should not be added as an afterthought after the endpoint is already built.</p>
<h2>Idempotency matters for some operations</h2>
<p>Some API operations may be called more than once because of retries, network failures, client timeouts, or user actions.</p>
<p>If the API is not designed carefully, repeated requests can create duplicate data or inconsistent behavior.</p>
<p>For example, if a client sends a payment request and does not receive a response due to a timeout, it may retry the same request.</p>
<p>Should the system create another payment?</p>
<p>Probably not.</p>
<p>That is where idempotency matters.</p>
<p>An idempotent operation can be safely repeated without changing the result beyond the first successful request.</p>
<p>Not every endpoint needs an idempotency strategy.</p>
<p>But for operations involving payments, order creation, external integrations, background processing, or retries, it should be considered early.</p>
<p>Good API design thinks about what happens when the same request is sent twice.</p>
<h2>Pagination, filtering, and sorting should be designed consistently</h2>
<p>List endpoints often start simple.</p>
<p><code>GET /orders</code></p>
<p>Then the data grows.</p>
<p>The client asks for pagination.</p>
<p>Then filtering.</p>
<p>Then sorting.</p>
<p>Then search.</p>
<p>Then date ranges.</p>
<p>Then status filters.</p>
<p>If these patterns are not designed consistently, every list endpoint becomes different.</p>
<p>One endpoint uses <code>page</code> and <code>pageSize</code>.</p>
<p>Another uses <code>skip</code> and <code>take</code>.</p>
<p>Another uses <code>limit</code> and <code>offset</code>.</p>
<p>One returns total count.</p>
<p>Another does not.</p>
<p>One supports sorting by <code>createdDate</code>.</p>
<p>Another supports <code>sortBy</code>.</p>
<p>This inconsistency makes APIs harder to use.</p>
<p>A good API defines list patterns early.</p>
<p>For example:</p>
<ul>
<li><p>consistent pagination parameters</p>
</li>
<li><p>maximum page size limits</p>
</li>
<li><p>clear filtering rules</p>
</li>
<li><p>supported sort fields</p>
</li>
<li><p>default ordering</p>
</li>
<li><p>response metadata</p>
</li>
<li><p>predictable empty results</p>
</li>
</ul>
<p>List endpoints are often used heavily in real applications, so small design decisions here can have a big impact.</p>
<h2>Failure behavior should be designed</h2>
<p>APIs do not only communicate success.</p>
<p>They also communicate failure.</p>
<p>A good API design considers what happens when something goes wrong.</p>
<p>For example:</p>
<ul>
<li><p>the request is invalid</p>
</li>
<li><p>the user is not authorized</p>
</li>
<li><p>the resource does not exist</p>
</li>
<li><p>the resource state does not allow the operation</p>
</li>
<li><p>the database is unavailable</p>
</li>
<li><p>an external API fails</p>
</li>
<li><p>a background process cannot be started</p>
</li>
<li><p>a timeout occurs</p>
</li>
<li><p>a concurrency conflict happens</p>
</li>
</ul>
<p>Each failure type should be handled intentionally.</p>
<p>Not every failure should become a generic <code>500 Internal Server Error</code>.</p>
<p>Some failures are expected business situations.</p>
<p>Some are validation issues.</p>
<p>Some are security-related.</p>
<p>Some are infrastructure problems.</p>
<p>Some should be retried.</p>
<p>Some should not.</p>
<p>Clear failure handling makes the API more reliable and easier to support.</p>
<h2>Backward compatibility is part of API design</h2>
<p>Once clients start using an API, changing it becomes harder.</p>
<p>Removing fields, renaming properties, changing response shapes, or changing status code behavior can break existing consumers.</p>
<p>That is why API design should consider backward compatibility.</p>
<p>A few useful principles:</p>
<ul>
<li><p>avoid exposing internal models directly</p>
</li>
<li><p>be careful when removing or renaming fields</p>
</li>
<li><p>prefer additive changes where possible</p>
</li>
<li><p>version APIs when necessary</p>
</li>
<li><p>document breaking changes clearly</p>
</li>
<li><p>avoid changing meaning without changing the contract</p>
</li>
</ul>
<p>Backward compatibility is especially important for APIs consumed by external clients, mobile apps, integrations, or multiple internal teams.</p>
<p>An endpoint may be easy to change in code.</p>
<p>But the API contract may already belong to its consumers.</p>
<h2>Observability should not be optional</h2>
<p>A good API should be easy to debug in production.</p>
<p>That means observability should be considered during design.</p>
<p>When an issue happens, developers should be able to answer questions like:</p>
<ul>
<li><p>Which endpoint failed?</p>
</li>
<li><p>Which request caused the issue?</p>
</li>
<li><p>Which user or tenant was involved?</p>
</li>
<li><p>What was the correlation ID?</p>
</li>
<li><p>Did validation fail?</p>
</li>
<li><p>Did authorization fail?</p>
</li>
<li><p>Which downstream dependency failed?</p>
</li>
<li><p>How long did the request take?</p>
</li>
<li><p>Was the database query slow?</p>
</li>
<li><p>Did a retry happen?</p>
</li>
<li><p>Was the response successful?</p>
</li>
</ul>
<p>Logs, metrics, traces, and correlation IDs are not just operational extras.</p>
<p>They are part of building a supportable API.</p>
<p>A production issue becomes much harder to resolve when the API returns an error but leaves no useful trail behind.</p>
<h2>Documentation improves trust</h2>
<p>An API that is hard to understand is hard to trust.</p>
<p>Documentation does not need to be perfect, but it should help another developer integrate without guessing.</p>
<p>Good API documentation should explain:</p>
<ul>
<li><p>endpoint purpose</p>
</li>
<li><p>request format</p>
</li>
<li><p>response format</p>
</li>
<li><p>required fields</p>
</li>
<li><p>status codes</p>
</li>
<li><p>validation errors</p>
</li>
<li><p>authentication requirements</p>
</li>
<li><p>authorization rules</p>
</li>
<li><p>pagination/filtering/sorting behavior</p>
</li>
<li><p>example requests and responses</p>
</li>
<li><p>known constraints</p>
</li>
</ul>
<p>Swagger/OpenAPI is useful, but generated documentation alone may not explain the business meaning of the API.</p>
<p>The best documentation combines technical structure with practical context.</p>
<h2>A simple API design checklist</h2>
<p>Before exposing an endpoint, it helps to ask a few questions.</p>
<ul>
<li><p>Is the endpoint name and HTTP method appropriate?</p>
</li>
<li><p>Is the request contract separate from internal models?</p>
</li>
<li><p>Does the response expose only what the client needs?</p>
</li>
<li><p>Are validation errors consistent?</p>
</li>
<li><p>Are status codes used correctly?</p>
</li>
<li><p>Are authentication and authorization rules clear?</p>
</li>
<li><p>Is idempotency needed?</p>
</li>
<li><p>Are pagination, filtering, and sorting consistent?</p>
</li>
<li><p>Are downstream failures handled properly?</p>
</li>
<li><p>Is the API backward compatible?</p>
</li>
<li><p>Are logs, traces, and correlation IDs available?</p>
</li>
<li><p>Is the endpoint documented well enough for another developer to use?</p>
</li>
</ul>
<p>This checklist does not guarantee perfect API design.</p>
<p>But it helps avoid many common mistakes.</p>
<h2>Final thoughts</h2>
<p>An API endpoint is not the same as API design.</p>
<p>Creating an endpoint is the easy part.</p>
<p>Designing an API that other developers can trust requires more thought.</p>
<p>Good API design considers contracts, validation, status codes, security, idempotency, failure handling, backward compatibility, observability, and documentation.</p>
<p>A successful API should be predictable, secure, consistent, and easy to consume.</p>
<p>One sign of a well-designed API is simple:</p>
<p>Another developer should be able to integrate with it without needing to ask too many questions.</p>
<p>That is the difference between exposing an endpoint and designing an API.</p>
<p>What is one API design mistake you often see in backend projects?</p>
]]></content:encoded></item><item><title><![CDATA[AI Can Generate Code, But It Cannot Fix Weak Engineering Fundamentals]]></title><description><![CDATA[AI can generate code quickly.
It can create an API endpoint, write a service method, suggest a database query, generate unit tests, explain an error message, or help refactor a class within seconds.
T]]></description><link>https://blog.devni.dev/ai-can-generate-code-but-it-cannot-fix-weak-engineering-fundamentals</link><guid isPermaLink="true">https://blog.devni.dev/ai-can-generate-code-but-it-cannot-fix-weak-engineering-fundamentals</guid><category><![CDATA[dotnet]]></category><category><![CDATA[C#]]></category><category><![CDATA[asp.net core]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Backend Development]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Devni Heraliyawala]]></dc:creator><pubDate>Fri, 12 Jun 2026 05:22:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a28fc1c0d3661f679c2132f/d22eff9c-b976-424f-bca8-ad5ebdaf742c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI can generate code quickly.</p>
<p>It can create an API endpoint, write a service method, suggest a database query, generate unit tests, explain an error message, or help refactor a class within seconds.</p>
<p>That is powerful.</p>
<p>But speed is not the same as engineering quality.</p>
<p>Because AI can help with implementation, but it cannot automatically fix weak engineering fundamentals.</p>
<p>That becomes very clear when code has to deal with real-world conditions.</p>
<p>Not the perfect demo case.</p>
<p>Not the happy path.</p>
<p>But the messy reality of production software.</p>
<h2>Real software is more than generated code</h2>
<p>In real applications, code has to handle many things that are easy to ignore in a simple example.</p>
<ul>
<li><p>Invalid input.</p>
</li>
<li><p>Authentication failures.</p>
</li>
<li><p>Authorization rules.</p>
</li>
<li><p>Slow database queries.</p>
</li>
<li><p>Race conditions.</p>
</li>
<li><p>Unclear business requirements.</p>
</li>
<li><p>Missing logs.</p>
</li>
<li><p>Poor error handling.</p>
</li>
<li><p>Broken retries.</p>
</li>
<li><p>Security gaps.</p>
</li>
<li><p>Unexpected production incidents.</p>
</li>
</ul>
<p>These are the areas where engineering fundamentals matter.</p>
<p>A generated code snippet may look correct at first glance. It may even compile. It may pass a simple test. But that does not mean it is production-ready.</p>
<p>As developers, we still need to understand whether the implementation is correct, secure, maintainable, scalable, and suitable for the system we are building.</p>
<p>That responsibility does not disappear because AI helped us write the code.</p>
<h2>For .NET developers, fundamentals still matter</h2>
<p>For .NET developers, fundamentals are still extremely important because most production problems are not solved by syntax alone.</p>
<p>They are solved by understanding how the platform behaves.</p>
<p>They are solved by knowing how ASP.NET Core handles requests.</p>
<p>They are solved by designing clean API contracts.</p>
<p>They are solved by modelling data properly.</p>
<p>They are solved by understanding async behaviour, cancellation, dependency injection, logging, testing, and security.</p>
<p>Some of the .NET fundamentals I think developers should revisit often include:</p>
<ul>
<li><p>C# behaviour</p>
</li>
<li><p>ASP.NET Core request pipelines</p>
</li>
<li><p>REST API contracts</p>
</li>
<li><p>SQL and data modelling</p>
</li>
<li><p>Entity Framework Core behaviour</p>
</li>
<li><p>Async/Await and cancellation</p>
</li>
<li><p>Authentication and Authorization</p>
</li>
<li><p>Dependency injection</p>
</li>
<li><p>Testing strategies</p>
</li>
<li><p>Logging and observability</p>
</li>
<li><p>Error handling</p>
</li>
<li><p>Debugging under pressure</p>
</li>
</ul>
<p>These are not just interview topics.</p>
<p>They are the skills that help us build systems that survive real usage.</p>
<h2>AI does not understand your production context by default</h2>
<p>One challenge with AI-generated code is that it often does not fully understand the production context around the problem.</p>
<p>It may not know your system boundaries.</p>
<p>It may not understand your business rules.</p>
<p>It may not know your existing architecture.</p>
<p>It may not know your logging standards.</p>
<p>It may not know how your authentication model works.</p>
<p>It may not understand your database performance constraints.</p>
<p>It may not know what kind of failure behaviour is acceptable in your domain.</p>
<p>That is why blindly accepting generated code is risky.</p>
<p>For example, AI may generate an API method that works for a simple request, but does not validate input properly.</p>
<p>It may suggest a database query that returns the correct data, but performs badly at scale.</p>
<p>It may create retry logic without considering idempotency.</p>
<p>It may write async code without cancellation support.</p>
<p>It may catch exceptions too broadly and hide important failures.</p>
<p>It may expose data that should not be returned from an API.</p>
<p>These are not small details.</p>
<p>These are engineering decisions.</p>
<h2>Engineering judgement is still the real skill</h2>
<p>AI tools can help us move faster, but engineering judgement decides whether the output is actually good.</p>
<p>A strong developer does not only ask:</p>
<p>“Does this code work?”</p>
<p>A strong developer also asks:</p>
<p>“Is this the right design?”</p>
<p>“Is this secure?”</p>
<p>“Can this fail safely?”</p>
<p>“Will this scale?”</p>
<p>“Is the API contract clear?”</p>
<p>“Can we debug this in production?”</p>
<p>“Does this follow our architecture?”</p>
<p>“Will another developer understand this later?”</p>
<p>“Are we adding unnecessary complexity?”</p>
<p>“Have we tested the important scenarios?”</p>
<p>This type of thinking is what separates code generation from software engineering.</p>
<p>And this is why fundamentals still matter.</p>
<h2>The production problems are usually not syntax problems</h2>
<p>Many real production issues are not caused by not knowing the syntax.</p>
<p>They are caused by weak assumptions.</p>
<p>A missing validation rule.</p>
<p>A poorly designed database query.</p>
<p>A race condition.</p>
<p>An unhandled null case.</p>
<p>A broken authentication flow.</p>
<p>A missing authorization check.</p>
<p>A background job that does not retry correctly.</p>
<p>An API that returns too much data.</p>
<p>A service that logs too little information.</p>
<p>A timeout that nobody planned for.</p>
<p>A deployment configuration that behaves differently from local development.</p>
<p>These problems require understanding.</p>
<p>They require investigation.</p>
<p>They require debugging.</p>
<p>They require knowledge of the system.</p>
<p>AI can assist in these moments, but it cannot replace the developer’s responsibility to reason through the problem.</p>
<h2>Strong fundamentals make AI more useful</h2>
<p>I do not see AI as a replacement for developers.</p>
<p>I see it as a multiplier.</p>
<p>But what it multiplies depends on the developer using it.</p>
<p>If the developer has weak fundamentals, AI may help produce weak code faster.</p>
<p>But if the developer understands the platform, architecture, security, testing, and production concerns, AI becomes much more useful.</p>
<p>It can help generate a first draft.</p>
<p>It can suggest alternative approaches.</p>
<p>It can explain unfamiliar APIs.</p>
<p>It can help create tests.</p>
<p>It can help review edge cases.</p>
<p>It can speed up documentation.</p>
<p>It can support debugging.</p>
<p>But the developer still needs to validate the result.</p>
<p>That is where strong fundamentals make the difference.</p>
<h2>The strongest developers in 2026</h2>
<p>I think the strongest developers in 2026 will not be the ones who only know how to use AI tools.</p>
<p>They will be the ones who can combine AI with strong engineering fundamentals.</p>
<p>They will know how to use AI to move faster, but they will also know when to slow down and think.</p>
<p>They will understand the difference between generated code and production-ready code.</p>
<p>They will be able to review, question, validate, and improve AI-generated output.</p>
<p>They will know how to design APIs, model data, handle failures, secure applications, write meaningful tests, and debug real issues.</p>
<p>That combination is powerful.</p>
<p>Not AI alone.</p>
<p>Not fundamentals alone.</p>
<p>But AI supported by strong engineering judgement.</p>
<h2>My focus as a .NET developer</h2>
<p>For me, this is why I want to keep strengthening the fundamentals.</p>
<p>I want to go deeper into C#, ASP.NET Core, API design, SQL, async programming, authentication, testing, logging, observability, and production debugging.</p>
<p>Not because these topics are new.</p>
<p>But because they are the foundation for everything else.</p>
<p>They are also what make AI tools more useful.</p>
<p>When the foundation is strong, AI can help us move faster without losing quality.</p>
<p>When the foundation is weak, AI can make mistakes look more polished.</p>
<p>That is the risk.</p>
<h2>Final thoughts</h2>
<p>AI can generate code quickly.</p>
<p>But it cannot fix weak engineering fundamentals.</p>
<p>For .NET developers, the foundation still matters because real software has to handle real-world conditions.</p>
<ul>
<li><p>Invalid input.</p>
</li>
<li><p>Authentication failures.</p>
</li>
<li><p>Slow queries.</p>
</li>
<li><p>Race conditions.</p>
</li>
<li><p>Unclear business rules.</p>
</li>
<li><p>Security gaps.</p>
</li>
<li><p>Production incidents.</p>
</li>
</ul>
<p>These problems require more than syntax.</p>
<p>They require engineering judgement.</p>
<p>AI is a multiplier.</p>
<p>But the foundation still matters.</p>
<p>The developers who grow the most in 2026 will be the ones who know how to combine AI tools with strong fundamentals.</p>
<p>That is the kind of engineer I want to become.</p>
<p>What .NET fundamental do you think developers should revisit more often?</p>
]]></content:encoded></item><item><title><![CDATA[In 2026, Being a .NET Developer Is Not Enough]]></title><description><![CDATA[For many years, being a good .NET developer meant being able to build web applications, write APIs, work with SQL Server, fix bugs, and deliver business features reliably.
Those skills still matter.
I]]></description><link>https://blog.devni.dev/in-2026-being-a-net-developer-is-not-enough</link><guid isPermaLink="true">https://blog.devni.dev/in-2026-being-a-net-developer-is-not-enough</guid><category><![CDATA[.NET]]></category><category><![CDATA[C#]]></category><category><![CDATA[Azure]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[AI]]></category><category><![CDATA[Career Growth]]></category><dc:creator><![CDATA[Devni Heraliyawala]]></dc:creator><pubDate>Wed, 10 Jun 2026 12:42:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6a28fc1c0d3661f679c2132f/9328ea6b-2912-4e50-9ec6-c4a6ad14d623.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For many years, being a good .NET developer meant being able to build web applications, write APIs, work with SQL Server, fix bugs, and deliver business features reliably.</p>
<p>Those skills still matter.</p>
<p>In fact, they matter more than ever.</p>
<p>But in 2026, being “just a .NET developer” is no longer enough.</p>
<p>Not because .NET is losing value.</p>
<p>The opposite is true.</p>
<p>.NET continues to be one of the strongest platforms for enterprise applications, APIs, backend systems, cloud services, integrations, automation platforms, and business-critical software.</p>
<p>The change is not about .NET becoming less important.</p>
<p>The change is about the expectations around software engineers becoming broader.</p>
<p>Modern companies do not only need developers who can complete tickets. They need engineers who can understand systems, make technical decisions, design reliable solutions, work with cloud infrastructure, and use AI responsibly to solve real business problems.</p>
<p>That is where the role of a .NET developer is changing.</p>
<h2>The role of a .NET developer is expanding</h2>
<p>In many enterprise environments, .NET is still used to build serious business systems.</p>
<p>These systems often include:</p>
<ul>
<li><p>Internal platforms</p>
</li>
<li><p>REST APIs</p>
</li>
<li><p>Background services</p>
</li>
<li><p>Authentication and Identity flows</p>
</li>
<li><p>Reporting systems</p>
</li>
<li><p>File-processing workflows</p>
</li>
<li><p>Automation tools</p>
</li>
<li><p>Cloud integrations</p>
</li>
<li><p>Customer-facing applications</p>
</li>
<li><p>Business-critical operations</p>
</li>
</ul>
<p>Building these systems well requires more than knowing C# syntax or ASP.NET Core basics.</p>
<p>A strong .NET developer needs to understand how applications behave in real environments.</p>
<p>How does the system handle invalid input?</p>
<p>How are authentication and authorization enforced?</p>
<p>How are long-running operations managed?</p>
<p>How are errors logged?</p>
<p>How are database queries optimized?</p>
<p>How does the system recover from failures?</p>
<p>How is the application deployed, monitored, and maintained?</p>
<p>How easy will it be for another developer to understand this code six months later?</p>
<p>These questions are not small details.</p>
<p>They are part of building reliable software.</p>
<h2>Writing code is not the same as building systems</h2>
<p>There is a difference between writing code and building systems.</p>
<p>Writing code focuses on implementing a requirement.</p>
<p>Building systems focuses on understanding how the requirement fits into the bigger picture.</p>
<p>A feature may work correctly in isolation but still create problems if the design is weak.</p>
<p>For example:</p>
<ul>
<li><p>an API can return the correct result but expose too much data</p>
</li>
<li><p>a database query can work locally but perform badly in production</p>
</li>
<li><p>a service method can compile but hide important exceptions</p>
</li>
<li><p>a background process can work once but fail when retried</p>
</li>
<li><p>an authentication flow can allow login but miss important authorization rules</p>
</li>
<li><p>an application can be deployed but have no useful logs when something breaks</p>
</li>
</ul>
<p>This is why modern .NET development requires more than implementation skill.</p>
<p>It requires engineering judgement.</p>
<h2>Fundamentals still come first</h2>
<p>With the rise of AI tools, agents, copilots, and automation, it is easy to feel that developers should rush into every new trend.</p>
<p>But strong fundamentals are still the foundation of good software engineering.</p>
<p>For .NET developers, the fundamentals include:</p>
<ul>
<li><p>C#</p>
</li>
<li><p>ASP.NET Core</p>
</li>
<li><p>REST API design</p>
</li>
<li><p>SQL and data modelling</p>
</li>
<li><p>Entity Framework Core</p>
</li>
<li><p>Async/Await</p>
</li>
<li><p>Dependency injection</p>
</li>
<li><p>Authentication and Authorization</p>
</li>
<li><p>Testing</p>
</li>
<li><p>Logging</p>
</li>
<li><p>Error handling</p>
</li>
<li><p>Debugging</p>
</li>
<li><p>Performance</p>
</li>
<li><p>Architecture principles</p>
</li>
</ul>
<p>These topics may not sound new or exciting, but they are the skills that matter when real systems fail.</p>
<p>AI can help generate code faster.</p>
<p>But AI does not automatically make code correct, secure, scalable, or maintainable.</p>
<p>If the foundation is weak, AI can simply help produce weak code faster.</p>
<p>That is why fundamentals are not becoming less important.</p>
<p>They are becoming more important.</p>
<h2>Architecture is becoming a core skill</h2>
<p>As developers become more experienced, the questions they need to answer become more complex.</p>
<p>At the beginning, the main question is often:</p>
<p>“How do I implement this feature?”</p>
<p>Later, the question becomes:</p>
<p>“Is this the right way to design this feature?”</p>
<p>That second question is where architecture begins.</p>
<p>Architecture is not only about diagrams, patterns, or using popular terms.</p>
<p>Architecture is about trade-offs.</p>
<p>Should this be a simple CRUD module or a separate service?</p>
<p>Should this logic stay in the API layer or move into the domain/application layer?</p>
<p>Is CQRS useful here, or would it add unnecessary complexity?</p>
<p>Should this be synchronous or asynchronous?</p>
<p>How should failures be handled?</p>
<p>Where should validation live?</p>
<p>How should data consistency be maintained?</p>
<p>How can the system remain understandable as it grows?</p>
<p>These decisions shape the long-term maintainability of a system.</p>
<p>A modern .NET developer does not need to over-engineer every solution.</p>
<p>But they do need to understand when design decisions matter.</p>
<h2>Cloud knowledge is no longer optional</h2>
<p>Modern .NET applications rarely live only on a developer’s machine or a single server.</p>
<p>They are deployed, configured, monitored, scaled, and maintained in cloud or hybrid environments.</p>
<p>For .NET developers, Azure is a natural area to understand because it connects closely with the Microsoft ecosystem.</p>
<p>Important areas include:</p>
<ul>
<li><p>Azure App Service</p>
</li>
<li><p>Azure Functions</p>
</li>
<li><p>Azure Blob Storage</p>
</li>
<li><p>Azure SQL</p>
</li>
<li><p>Azure Service Bus</p>
</li>
<li><p>Azure Key Vault</p>
</li>
<li><p>Application Insights</p>
</li>
<li><p>CI/CD pipelines</p>
</li>
<li><p>Docker</p>
</li>
<li><p>distributed application development</p>
</li>
</ul>
<p>Cloud knowledge changes how developers think about software.</p>
<p>It encourages thinking about configuration, secrets, deployment pipelines, observability, scalability, reliability, and operational responsibility.</p>
<p>This matters because real software does not end when the code is merged.</p>
<p>Real software has to run.</p>
<p>And when it runs, it has to be monitored, supported, secured, and improved.</p>
<h2>Practical AI is becoming part of application development</h2>
<p>AI is changing software development in two important ways.</p>
<p>The first is AI-assisted development.</p>
<p>Developers can now use AI tools to generate code, write tests, explain errors, review code, create documentation, and explore unfamiliar APIs faster.</p>
<p>But this still requires discipline.</p>
<p>Generated code must be reviewed.</p>
<p>Security must be checked.</p>
<p>Performance must be considered.</p>
<p>Tests must be written.</p>
<p>Business rules must be validated.</p>
<p>Architecture must still make sense.</p>
<p>The second area is AI integration inside business applications.</p>
<p>This is where .NET developers have a major opportunity.</p>
<p>Not every company needs a deep learning engineer.</p>
<p>But many companies need engineers who can integrate useful AI capabilities into existing business systems.</p>
<p>Examples include:</p>
<ul>
<li><p>document extraction</p>
</li>
<li><p>support ticket summarization</p>
</li>
<li><p>internal knowledge search</p>
</li>
<li><p>customer service assistants</p>
</li>
<li><p>reporting assistants</p>
</li>
<li><p>data classification</p>
</li>
<li><p>workflow automation</p>
</li>
<li><p>RAG-based search over company documents</p>
</li>
<li><p>AI-assisted validation and review flows</p>
</li>
</ul>
<p>This is the practical side of AI.</p>
<p>Not AI for hype.</p>
<p>AI for real business problems.</p>
<h2>The modern .NET developer profile</h2>
<p>The modern .NET developer profile is broader than before.</p>
<p>It is not enough to only know how to write controllers, services, and database queries.</p>
<p>A strong .NET engineer should be able to work across multiple layers of software delivery.</p>
<p>That includes:</p>
<ul>
<li><p>writing clean and maintainable C# code</p>
</li>
<li><p>designing reliable APIs</p>
</li>
<li><p>understanding architecture trade-offs</p>
</li>
<li><p>working with databases efficiently</p>
</li>
<li><p>securing applications properly</p>
</li>
<li><p>using cloud services effectively</p>
</li>
<li><p>debugging production issues</p>
</li>
<li><p>improving observability</p>
</li>
<li><p>automating delivery through CI/CD</p>
</li>
<li><p>reviewing AI-generated code responsibly</p>
</li>
<li><p>integrating practical AI features into real systems</p>
</li>
</ul>
<p>This does not mean every developer must become an expert in everything.</p>
<p>But it does mean the role is expanding.</p>
<p>The strongest developers will be the ones who can connect implementation with system thinking.</p>
<h2>Senior-level value is moving beyond syntax</h2>
<p>At junior level, syntax and implementation speed matter a lot.</p>
<p>At senior and technical lead level, the value becomes broader.</p>
<p>The important questions become:</p>
<ul>
<li><p>Is this design maintainable?</p>
</li>
<li><p>Does this solution match the business problem?</p>
</li>
<li><p>Can the system handle failure?</p>
</li>
<li><p>Is the API contract clear?</p>
</li>
<li><p>Are security rules enforced correctly?</p>
</li>
<li><p>Can this be tested?</p>
</li>
<li><p>Can this be deployed safely?</p>
</li>
<li><p>Can this be monitored in production?</p>
</li>
<li><p>Can another developer understand and extend this later?</p>
</li>
<li><p>Are we solving the problem simply, or adding unnecessary complexity?</p>
</li>
</ul>
<p>These questions are what separate coding from engineering.</p>
<p>Modern .NET developers need to become comfortable with these decisions.</p>
<h2>What developers should focus on in 2026</h2>
<p>For .NET developers who want to grow in 2026, four areas are especially important.</p>
<p>First, .NET engineering depth.</p>
<p>This includes C#, ASP.NET Core, EF Core, API design, authentication, testing, async programming, logging, and performance.</p>
<p>Second, architecture and system design.</p>
<p>This includes understanding modular design, Clean Architecture principles, CQRS trade-offs, integration patterns, background processing, reliability, and maintainability.</p>
<p>Third, Azure and cloud-native development.</p>
<p>This includes deployment, configuration, storage, messaging, monitoring, secrets management, and operational thinking.</p>
<p>Fourth, practical AI integration.</p>
<p>This includes using AI tools responsibly as a developer and understanding how to add useful AI features into business applications.</p>
<p>Together, these areas create a stronger and more future-ready engineering profile.</p>
<h2>Final thoughts</h2>
<p>In 2026, being a .NET developer is still valuable.</p>
<p>But being only a .NET developer in the narrow sense is not enough.</p>
<p>The opportunity is to go deeper.</p>
<p>Deeper into fundamentals.</p>
<p>Deeper into architecture.</p>
<p>Deeper into cloud.</p>
<p>Deeper into practical AI.</p>
<p>The strongest developers will not be the ones who chase every trend.</p>
<p>They will be the ones who understand the fundamentals, make good technical decisions, build reliable systems, and use modern tools responsibly.</p>
<p>.NET is still a powerful platform.</p>
<p>But the expectations around .NET developers are changing.</p>
<p>The future belongs to engineers who can do more than write code from requirements.</p>
<p>It belongs to engineers who can build systems that are secure, maintainable, scalable, observable, and useful.</p>
]]></content:encoded></item></channel></rss>