<?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: Rodrigo Vidal</title>
    <description>The latest articles on DEV Community by Rodrigo Vidal (@rodrigovidal).</description>
    <link>https://dev.to/rodrigovidal</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%2F195536%2F92afd0be-7172-4c9e-9dad-5a8e4b9580e2.jpg</url>
      <title>DEV Community: Rodrigo Vidal</title>
      <link>https://dev.to/rodrigovidal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rodrigovidal"/>
    <language>en</language>
    <item>
      <title>Introducing Nugetz.dev — We Built a Better Way to Search NuGet</title>
      <dc:creator>Rodrigo Vidal</dc:creator>
      <pubDate>Wed, 07 Oct 2026 00:58:30 +0000</pubDate>
      <link>https://dev.to/rodrigovidal/introducing-nugetzdev-we-built-a-better-way-to-search-nuget-lbp</link>
      <guid>https://dev.to/rodrigovidal/introducing-nugetzdev-we-built-a-better-way-to-search-nuget-lbp</guid>
      <description>&lt;p&gt;Last month I shipped &lt;a href="https://nugetz.dev" rel="noopener noreferrer"&gt;Nugetz.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a modern interface for searching the NuGet registry.&lt;/p&gt;

&lt;p&gt;This isn’t a startup launch.&lt;br&gt;&lt;br&gt;
It’s not a product pivot.&lt;br&gt;&lt;br&gt;
It’s not a monetization play.&lt;/p&gt;

&lt;p&gt;It’s just something that bothered me for a while.&lt;br&gt;&lt;br&gt;
And it was heavily inspired by what the community did with &lt;strong&gt;npmx. A&lt;/strong&gt; cleaner, faster way to explore npm.&lt;br&gt;&lt;br&gt;
That project made me think: why don’t we have something like this for NuGet?&lt;/p&gt;

&lt;p&gt;So I built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Frustration
&lt;/h2&gt;

&lt;p&gt;NuGet is massive. It powers a huge part of the .NET ecosystem.&lt;/p&gt;

&lt;p&gt;But the search experience hasn’t really kept up.&lt;/p&gt;

&lt;p&gt;When I’m evaluating packages, I want to move fast. I want to quickly scan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is this maintained?&lt;/li&gt;
&lt;li&gt;How often is it updated?&lt;/li&gt;
&lt;li&gt;How widely is it used?&lt;/li&gt;
&lt;li&gt;Does it look abandoned?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead, I often feel like I’m fighting the interface.&lt;/p&gt;

&lt;p&gt;Search should feel instant.&lt;br&gt;&lt;br&gt;
Scanning results should feel effortless.&lt;br&gt;&lt;br&gt;
Comparing packages should feel natural.&lt;/p&gt;

&lt;p&gt;So I built the version I wished existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Nugetz Is (and Isn’t)
&lt;/h2&gt;

&lt;p&gt;Nugetz is intentionally simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast search&lt;/li&gt;
&lt;li&gt;Clean layout&lt;/li&gt;
&lt;li&gt;Minimal UI noise&lt;/li&gt;
&lt;li&gt;Focus on readability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No login.&lt;br&gt;&lt;br&gt;
No ads.&lt;br&gt;&lt;br&gt;
No growth hacks.&lt;/p&gt;

&lt;p&gt;Just a better way to explore packages.&lt;/p&gt;

&lt;p&gt;It’s not trying to replace NuGet.&lt;br&gt;&lt;br&gt;
It’s not trying to compete with Microsoft.&lt;/p&gt;

&lt;p&gt;It’s just a sharper interface on top of the registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Bother?
&lt;/h2&gt;

&lt;p&gt;Because developer tooling matters.&lt;/p&gt;

&lt;p&gt;We spend hours every week inside search pages, documentation, package registries, dashboards.&lt;/p&gt;

&lt;p&gt;Small UX improvements compound.&lt;/p&gt;

&lt;p&gt;If something saves you even a few seconds each time you search, over months and years, that adds up.&lt;/p&gt;

&lt;p&gt;Good developer experience is leverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I’d Love
&lt;/h2&gt;

&lt;p&gt;If you use .NET, try it.&lt;/p&gt;

&lt;p&gt;Break it.&lt;br&gt;&lt;br&gt;
Tell me what’s missing.&lt;br&gt;&lt;br&gt;
Tell me what feels better.&lt;br&gt;&lt;br&gt;
Tell me what doesn’t.&lt;/p&gt;

&lt;p&gt;The .NET ecosystem deserves good tooling.&lt;br&gt;&lt;br&gt;
Even small improvements matter.&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nugetz.dev" rel="noopener noreferrer"&gt;https://nugetz.dev&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://rodrigovidal.substack.com/p/introducing-nugetzdev-we-built-a" rel="noopener noreferrer"&gt;Originally published on Substack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>showdev</category>
      <category>nuget</category>
    </item>
    <item>
      <title>A Queue Is a Promise About the Future</title>
      <dc:creator>Rodrigo Vidal</dc:creator>
      <pubDate>Wed, 07 Oct 2026 00:56:56 +0000</pubDate>
      <link>https://dev.to/rodrigovidal/a-queue-is-a-promise-about-the-future-536b</link>
      <guid>https://dev.to/rodrigovidal/a-queue-is-a-promise-about-the-future-536b</guid>
      <description>&lt;p&gt;&lt;em&gt;Accepting work is easy. Finishing it on time is the promise.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An application does not have to finish everything at the same moment. A customer can place an order before the confirmation email arrives. A file can be uploaded before its contents have been processed. A report can be requested before the calculations are complete. There is real engineering value in separating the moment we accept an operation from the moment we finish all the work associated with it.&lt;/p&gt;

&lt;p&gt;Queues give us a useful way to make that separation explicit. They let one part of a system hand work to another without requiring both parts to move at exactly the same pace. They can absorb a temporary burst, allow work to be retried, and keep an immediate request from waiting for something that can reasonably happen later.&lt;/p&gt;

&lt;p&gt;But I think the word “later” deserves much more attention than it usually receives.&lt;/p&gt;

&lt;p&gt;Because later can mean a few milliseconds. It can mean several minutes. It can also mean tomorrow, or a point in the future that the system currently has no realistic capacity to reach. Those are very different promises, even when the architecture uses the same queue and the same consumers.&lt;/p&gt;

&lt;p&gt;In earlier posts, I wrote about how boundaries change the physical conditions of an operation and how structure should follow the responsibilities a system actually has. A queue adds another dimension to that discussion. It changes the relationship between the system and time. We can respond before the work finishes, but the unfinished work remains somewhere, waiting for resources to become available.&lt;/p&gt;

&lt;p&gt;Imagine a system accepting three hundred operations per second while its workers can finish two hundred and fifty. If every operation is accepted and nothing is discarded, the backlog grows by fifty operations every second. After one minute, there are three thousand additional operations waiting. The queue might be functioning perfectly. Every message might be stored correctly. Nothing about that prevents the system from falling further behind.&lt;/p&gt;

&lt;p&gt;The queue is doing exactly what we asked it to do. It is holding the difference between what we promised and what we can currently deliver.&lt;/p&gt;

&lt;p&gt;That can be a very sensible arrangement for a temporary burst. Demand rises, work accumulates, demand falls, and the available processing capacity eventually clears the backlog. But clearing it requires spare capacity. Returning to a point where arrivals and completions are equal only stops the backlog from growing. The work already waiting still needs time and resources beyond those required to handle new arrivals.&lt;/p&gt;

&lt;p&gt;And I think this is where a lot of conversations about asynchronous architecture become disconnected from the underlying mechanics. We discuss the ability to accept more work as if it were the ability to complete more work. We increase the capacity of the buffer and describe the system as more scalable, even though the part responsible for processing the work has not become any faster.&lt;/p&gt;

&lt;p&gt;A larger queue can buy us more time. How much time is useful depends on how long the business can afford to wait.&lt;/p&gt;

&lt;p&gt;There are real ways that moving work into a queue can improve throughput. Workers might process items in batches, avoid repeated setup costs, or use resources more effectively. Those benefits come from changing how the work executes. They still need to be understood and measured. Storing more unfinished work, by itself, does not increase the rate at which that work gets completed.&lt;/p&gt;

&lt;p&gt;Physics still applies. A processor has limited execution capacity. A database has limited throughput. An external provider might accept only a certain rate of requests. The consumer can be constrained by any of those things, and the queue cannot make the constraint disappear. It gives us a place to wait while that constraint determines how quickly progress is possible.&lt;/p&gt;

&lt;p&gt;The engineering question is what kind of waiting we are willing to accept.&lt;/p&gt;

&lt;p&gt;For some operations, delay barely matters. An internal analytics update might be useful several minutes after the event occurred. For others, delay changes the value of the operation itself. A report needed before a meeting can become useless after the meeting ends. A notification about something happening now can become confusing when it arrives much later. An order awaiting fulfillment is still part of the customer’s experience even if the request that created it returned immediately.&lt;/p&gt;

&lt;p&gt;I think the business meaning of the operation should determine its time budget. Calling work asynchronous tells us something about how execution is organized. It tells us very little about when the result needs to exist.&lt;/p&gt;

&lt;p&gt;This also changes how we should understand the response we give the user. Accepting a request to generate a report means we have accepted responsibility for trying to produce it. It does not mean the report exists. A useful product needs to make that distinction understandable, through status, an expected delay, a notification, or a clear failure state. Otherwise, a fast response can leave the user with an inaccurate understanding of what the system has accomplished.&lt;/p&gt;

&lt;p&gt;And that distinction should exist in our measurements too. The endpoint can respond quickly while the operation takes an hour to finish. The broker can be available while consumers make almost no progress. A dashboard focused on request latency and infrastructure health can look reassuring while customers wait for results that are becoming increasingly late.&lt;/p&gt;

&lt;p&gt;We need to understand the time between accepting work and completing the useful outcome. The age of waiting work matters. So does the rate at which the backlog grows or shrinks. Even the number of queued items needs context, because a thousand inexpensive operations and a thousand expensive operations can represent very different amounts of remaining work.&lt;/p&gt;

&lt;p&gt;Adding workers can help when there is available capacity for them to use. But if every worker depends on the same saturated database or the same limited external service, more consumers can create more competition without completing much more work. We have increased the number of things trying to move through the constraint. We have not necessarily changed the constraint itself.&lt;/p&gt;

&lt;p&gt;Retries can make this more difficult. They are useful when a failure is temporary and another attempt has a reasonable chance of succeeding. But retrying against an already overloaded dependency adds more demand to a resource that is struggling with the demand it already has. The original business traffic is now competing with repeated attempts to finish earlier traffic.&lt;/p&gt;

&lt;p&gt;The recovery mechanism can become part of the overload.&lt;/p&gt;

&lt;p&gt;That is why accepting work needs a policy. Sometimes we should delay producers. Sometimes we should reject new requests explicitly. Sometimes we should prioritize important operations, expire work that has lost its value, or combine updates when only the latest state matters. These choices depend on the meaning of the work. Quietly dropping an obsolete cache refresh and losing a payment operation are very different decisions.&lt;/p&gt;

&lt;p&gt;The queue does not make those decisions for us.&lt;/p&gt;

&lt;p&gt;I think this is another place where the relationship between physics, engineering, and architecture becomes concrete. Physics constrains the rate at which the system can finish work. Engineering decides what delay is acceptable, how much capacity is needed, and how the system should behave when demand exceeds it. Architecture expresses those decisions through queues, workers, admission rules, and recovery mechanisms.&lt;/p&gt;

&lt;p&gt;If we choose the queue first and leave those decisions for later, we have created a place to store promises without understanding how we intend to keep them.&lt;/p&gt;

&lt;p&gt;A queue can be an excellent engineering tool. It gives us room to handle variation, organize execution, and make useful trade-offs around time. But the customer eventually needs the outcome, and the value of the architecture depends on whether that outcome arrives when it still matters.&lt;/p&gt;

&lt;p&gt;A queue holds work for the future.&lt;/p&gt;

&lt;p&gt;Good engineering makes sure that future is one the system can actually deliver.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://rodrigovidal.substack.com/p/a-queue-is-a-promise-about-the-future" rel="noopener noreferrer"&gt;Originally published on Substack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Every Boundary Has a Cost</title>
      <dc:creator>Rodrigo Vidal</dc:creator>
      <pubDate>Wed, 07 Oct 2026 00:53:48 +0000</pubDate>
      <link>https://dev.to/rodrigovidal/every-boundary-has-a-cost-1cmo</link>
      <guid>https://dev.to/rodrigovidal/every-boundary-has-a-cost-1cmo</guid>
      <description>&lt;p&gt;&lt;em&gt;What separation buys us, and what the running system pays for it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Something I keep noticing in software discussions is how easily we talk about boundaries as if they were inherently beneficial. Separate the responsibilities. Decouple the components. Extract the service. Put a queue between them. Give each domain its own database. The direction is almost always toward more separation, and the separation itself is often presented as evidence that the system is becoming better designed.&lt;/p&gt;

&lt;p&gt;But a boundary can change the physical constraints of the problem. Work that once happened inside a process can now require data to move between machines. We introduce latency, communication overhead, and coordination requirements into the same business operation. And depending on where we put that boundary, those changes can be surprisingly expensive.&lt;/p&gt;

&lt;p&gt;In my previous post, I wrote about the distinction between physics, engineering, and architecture, and how I think the industry often approaches them in the wrong order. Architecture becomes the starting point, while the underlying constraints become something we discover later, usually when the system is already difficult to change. I think the way we discuss boundaries is one of the clearest examples of that inversion.&lt;/p&gt;

&lt;p&gt;Because on a diagram, a boundary is almost free. You draw another box, move a responsibility into it, and connect it to the rest of the system with an arrow. The diagram becomes more organized. The responsibilities become more explicit. Everything looks a little more intentional.&lt;/p&gt;

&lt;p&gt;But in the running system, that arrow has to become something.&lt;/p&gt;

&lt;p&gt;Sometimes it becomes a function call. Sometimes it becomes a network request. Sometimes it becomes a message that sits in a queue until another process is available to handle it. Those are very different mechanisms, with very different costs, even when the diagrams look almost identical.&lt;/p&gt;

&lt;p&gt;And I think we often spend much more time discussing the boxes than understanding what we have placed inside the arrows.&lt;/p&gt;

&lt;p&gt;Imagine a fairly ordinary application where a customer places an order. The system checks inventory, calculates the price, processes payment, records the order, and eventually sends a confirmation email. There are business rules involved, there is state to coordinate, and there are external dependencies. It is already an engineering problem before we introduce any particular architectural style.&lt;/p&gt;

&lt;p&gt;Now imagine that inventory, pricing, payments, and orders all become separate services.&lt;/p&gt;

&lt;p&gt;Maybe that is the correct decision. But the customer still wants to perform the same operation. We have not reduced the amount of agreement the business requires between those responsibilities. We have changed the mechanisms through which that agreement has to happen.&lt;/p&gt;

&lt;p&gt;Checking inventory now involves serialization, communication, a timeout, and a decision about what to do when the answer does not arrive. Reserving inventory introduces another problem: the reservation might have succeeded even if the caller never received the response. Retrying the request now requires us to understand whether repeating the operation is safe. Recording the order in one database while updating inventory in another requires us to define what happens when one succeeds and the other fails.&lt;/p&gt;

&lt;p&gt;That is how an architectural decision reaches down into the physics of the system. We have introduced communication and data movement that the operation previously avoided. Engineering now has to account for those costs and the uncertainty they introduce. The business operation still needs to make sense. The architecture does not get to negotiate that away.&lt;/p&gt;

&lt;p&gt;And that is where I think a lot of the language around decoupling becomes misleading. We separate the code, the processes, the repositories, and the databases, and then talk as if we have separated the underlying responsibilities of the business itself. But if an order requires an inventory reservation, that relationship still exists. If checkout requires a response from the pricing service, that dependency still exists. It has simply taken a different form.&lt;/p&gt;

&lt;p&gt;Two components can be physically separated while remaining deeply dependent on each other.&lt;/p&gt;

&lt;p&gt;In some cases, that dependency becomes harder to manage precisely because of the separation. What used to be enforced through a local transaction now requires a recovery process. What used to be checked by the compiler now depends on compatibility between independently deployed applications. What used to be a straightforward call stack now requires enough observability to reconstruct what happened across several machines.&lt;/p&gt;

&lt;p&gt;These are all solvable problems. But solving them is work. And the work exists because of a decision we made.&lt;/p&gt;

&lt;p&gt;I think that distinction matters because the cost of a boundary tends to become invisible once the boundary is accepted as part of the architecture. The retries, the tracing, the message handlers, the reconciliation jobs, the deployment coordination, and the operational procedures become things the system simply “needs.” We stop asking which of those needs came from the business and which came from the way we chose to organize its implementation.&lt;/p&gt;

&lt;p&gt;At some point, a considerable portion of the system can exist to maintain the conditions required by the system itself.&lt;/p&gt;

&lt;p&gt;That does not make the architecture wrong. But it should make us curious about what those conditions are buying us.&lt;/p&gt;

&lt;p&gt;Sometimes they buy something extremely valuable. A workload might need to scale independently. A component might need stronger resource isolation. A team might need to deploy changes without coordinating every release with several other teams. A security requirement might demand a separate execution environment. These are real constraints, and a service boundary can be a very reasonable response to them.&lt;/p&gt;

&lt;p&gt;But the benefit should be concrete enough to discuss alongside the cost.&lt;/p&gt;

&lt;p&gt;“Inventory is a separate business concept” tells me something about how the code might be organized. It does not, by itself, tell me why checking inventory should require a network request. A concept can have a clear interface, strong encapsulation, and explicit ownership inside the same process. Logical separation and physical separation are different decisions, even though architectural conversations often collapse them into one.&lt;/p&gt;

&lt;p&gt;And this is also where I think the argument needs some balance. The machine is not the only thing whose constraints matter.&lt;/p&gt;

&lt;p&gt;People have limits too. Teams have limited attention. Understanding a large codebase takes time. Coordinating changes across several groups can become expensive. A system that minimizes every runtime cost while making ordinary development painfully difficult is not automatically a well-engineered system.&lt;/p&gt;

&lt;p&gt;Sometimes paying more at runtime is a reasonable way to reduce the cost of changing the software. Sometimes operational complexity is worth accepting because a team needs a degree of independence that the current structure cannot provide. Good engineering includes those trade-offs.&lt;/p&gt;

&lt;p&gt;But human benefits deserve the same scrutiny as technical ones. Giving each team a service does not automatically give each team autonomy. If every useful change still requires coordinated updates across those services, we may have preserved the organizational dependency while adding a distributed system around it.&lt;/p&gt;

&lt;p&gt;The names of the repositories do not determine how independently people can work.&lt;/p&gt;

&lt;p&gt;Queues create a similar kind of confusion. They can be extremely useful when an operation does not need to complete immediately. Sending a confirmation email or updating analytics probably does not need to hold up the customer’s request. Moving that work into the background can be a sensible decision.&lt;/p&gt;

&lt;p&gt;But accepting work and completing work are different promises. A queue gives us a place to hold work. We still need enough processing capacity to finish it, a way to understand how far behind it is, and a policy for what happens when processing repeatedly fails. The customer’s experience depends on whether the operation actually finishes within an acceptable amount of time.&lt;/p&gt;

&lt;p&gt;A queue changes when the system does the work, but the work still has to be done. Physics still constrains how quickly consumers can process it. Engineering has to decide how much delay the business can tolerate and what capacity is needed to keep up. Calling something asynchronous does not make its consequences disappear.&lt;/p&gt;

&lt;p&gt;The more I think about these decisions, the more I feel that the useful question is what a particular boundary allows us to do that we could not do adequately without it. Which constraint does it address? Which failure does it contain? Which coordination problem does it reduce? And what new problems do we accept in exchange?&lt;/p&gt;

&lt;p&gt;The answer can change over time. A boundary that would be unnecessary today might become valuable as the workload grows or the organization changes. A boundary that once solved an important problem might eventually become an expensive historical artifact. Architecture has to remain open to that possibility, because the conditions that justified it are rarely permanent.&lt;/p&gt;

&lt;p&gt;I think that is part of what makes engineering difficult. Adding a service is visible. Introducing a messaging platform is visible. A diagram with more components can look like progress. Understanding that a particular separation has no useful purpose requires a different kind of work, and removing it often requires more knowledge of the system than introducing it did.&lt;/p&gt;

&lt;p&gt;The best decisions connect all three layers. We understand the physical costs, make an engineering judgment about which costs are worth paying, and let the architecture reflect that judgment. The boundary becomes a consequence of understanding the problem, and we can explain both the responsibilities it separates and the cooperation it still requires.&lt;/p&gt;

&lt;p&gt;Every boundary has a cost.&lt;/p&gt;

&lt;p&gt;Sometimes that cost buys us something we need.&lt;/p&gt;

&lt;p&gt;Good engineering is being able to explain what that something is.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://rodrigovidal.substack.com/p/every-boundary-has-a-cost" rel="noopener noreferrer"&gt;Originally published on Substack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Cost of Architectural Symmetry</title>
      <dc:creator>Rodrigo Vidal</dc:creator>
      <pubDate>Wed, 07 Oct 2026 00:20:17 +0000</pubDate>
      <link>https://dev.to/rodrigovidal/the-cost-of-architectural-symmetry-2a03</link>
      <guid>https://dev.to/rodrigovidal/the-cost-of-architectural-symmetry-2a03</guid>
      <description>&lt;p&gt;&lt;em&gt;Why different responsibilities deserve different amounts of structure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is something reassuring about opening a codebase and recognizing its structure. The controllers are where we expect them to be. The services follow a familiar convention. Before we understand the details, we already have a sense of how to move through the system. I think that predictability explains a lot of our attachment to consistency.&lt;/p&gt;

&lt;p&gt;But familiar structure can also make us less likely to examine what each layer contributes. A controller calls a service, which calls another service, which calls a repository. The dependencies point in the correct direction. Everything has a place. And yet a small operation can pass through an impressive amount of architecture before anything happens beyond passing the request along.&lt;/p&gt;

&lt;p&gt;In my previous post, I wrote about boundaries that change the physical conditions under which work happens. Moving an operation between processes introduces communication, data movement, and uncertainty. Layers inside a process usually have a much smaller execution cost. But some of their cost is paid by the person trying to understand the system.&lt;/p&gt;

&lt;p&gt;Somebody still has to follow the operation, find the business rule, and determine where a change belongs. When several components pass the same arguments along, understanding the behavior means carrying that entire chain in your head.&lt;/p&gt;

&lt;p&gt;The machine might move through those calls cheaply. The engineer still has to make sense of them.&lt;/p&gt;

&lt;p&gt;There are reasonable reasons to introduce layers. A controller can translate an HTTP request into an application operation. A service can coordinate business behavior. A repository can isolate persistence details. Even a thin layer can establish a useful contract or contain a dependency that changes.&lt;/p&gt;

&lt;p&gt;But the responsibility should explain the layer. I feel like we often start with the layers and then try to find responsibilities to put inside them.&lt;/p&gt;

&lt;p&gt;Consider an endpoint that reads a customer’s preferred language. To follow that read, we open a controller, a service interface, a service implementation, a repository interface, and a repository. The service simply forwards the customer ID and returns the result. When the lookup needs an account ID as well, that extra argument travels through every signature and forwarding call. Only the query uses it to do anything. Several files change to support a decision made in one place, because the simple read inherited the structure of a more complicated operation.&lt;/p&gt;

&lt;p&gt;And I think a lot of this comes from our attachment to symmetry.&lt;/p&gt;

&lt;p&gt;Once one feature has a controller, a service, and a repository, a feature that only needs two of those starts to look incomplete. A straightforward read suddenly needs a place for business rules it does not have. So we create a service that passes the request along. The absence of a layer feels like an inconsistency, even when there is no responsibility for it to own.&lt;/p&gt;

&lt;p&gt;We are creating code to preserve the appearance of the architecture.&lt;/p&gt;

&lt;p&gt;The architecture of a house offers an interesting comparison. A bathroom needs plumbing and waterproofing. A kitchen needs ventilation and space for equipment. A bedroom has different requirements around privacy, light, and comfort. These rooms follow shared construction standards, but their structures differ because their purposes differ.&lt;/p&gt;

&lt;p&gt;Nobody expects a bedroom to contain the same fixtures as a bathroom to make the building more consistent.&lt;/p&gt;

&lt;p&gt;Software should allow that kind of variation too. Reading a value and coordinating inventory, payment, and order creation are different operations. Their structures can reflect those differences while following understandable conventions. Forcing both through the same sequence of components makes the simple operation harder to follow without necessarily making the complicated one clearer.&lt;/p&gt;

&lt;p&gt;The problem becomes more significant when the same expectation extends across entire systems. An organization creates a template, defines the approved layers, and expects every application to follow it. A small internal tool, a payment system, and a high-volume data pipeline inherit the same architecture before their requirements have been understood.&lt;/p&gt;

&lt;p&gt;This is where physics, engineering, and architecture become especially important. One system might spend most of its time waiting for external responses. Another might be constrained by memory bandwidth or contention around shared state. These physical constraints call for different engineering decisions, and those decisions should shape the architecture.&lt;/p&gt;

&lt;p&gt;The teams and operational requirements differ too. A boundary that helps several independent teams work can be excessive for an application maintained by two people.&lt;/p&gt;

&lt;p&gt;But when the template comes first, architecture becomes policy. Engineering becomes the work of fitting the problem into it. Physics becomes something we encounter when the approved structure performs differently from what we expected.&lt;/p&gt;

&lt;p&gt;The order has been inverted again.&lt;/p&gt;

&lt;p&gt;Templates can still preserve useful knowledge. Shared approaches to logging, configuration, deployment, and observability remove repeated work. Common conventions can reduce the burden of maintaining several systems. Those are real engineering benefits. But they need to justify the choices in the template. A standard does not give every component a useful role in every application.&lt;/p&gt;

&lt;p&gt;There is also a recurring argument behind many of these layers: we might need them later. The operation might acquire business rules. The dependency might need to be replaced. Sometimes that preparation is sensible, especially when a likely change would be expensive to accommodate.&lt;/p&gt;

&lt;p&gt;But the extra components already need to be understood and maintained. We are paying for flexibility before we know which kind we will need. Engineering has to distinguish between a change we have reason to expect and a possibility that is merely easy to imagine.&lt;/p&gt;

&lt;p&gt;The more I work on systems, the more I feel that useful architecture makes the important decisions easier to see. It gives a responsibility a clear home and makes complicated behavior understandable. Its value comes from what people can do with the system because that structure exists.&lt;/p&gt;

&lt;p&gt;Different parts of a codebase can need different amounts of structure. Different systems can need different architectures. Understanding those differences is part of the engineering work.&lt;/p&gt;

&lt;p&gt;The rooms belong to the same house. Their purposes justify their differences.&lt;/p&gt;

&lt;p&gt;A layer should exist because the system needs what it provides.&lt;/p&gt;

&lt;p&gt;Symmetry alone is a surprisingly expensive reason to write code.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://rodrigovidal.substack.com/p/the-cost-of-architectural-symmetry" rel="noopener noreferrer"&gt;Originally published on Substack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Physics, Engineering, and Architecture in Software Systems and the obsession with Architecture</title>
      <dc:creator>Rodrigo Vidal</dc:creator>
      <pubDate>Thu, 04 Jun 2026 12:02:50 +0000</pubDate>
      <link>https://dev.to/rodrigovidal/physics-engineering-and-architecture-in-software-systems-and-the-obsession-with-architecture-68j</link>
      <guid>https://dev.to/rodrigovidal/physics-engineering-and-architecture-in-software-systems-and-the-obsession-with-architecture-68j</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fb4s57skhp3gnwla6kr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7fb4s57skhp3gnwla6kr.png" alt=" " width="696" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Something that has been bothering me for a while in the software industry is how disproportionately obsessed we became with architecture. Not engineering. Not systems. Not the underlying mechanics of computation itself. Architecture.&lt;/p&gt;

&lt;p&gt;You can see it everywhere. Developers spend endless amounts of time discussing microservices, event-driven systems, clean architecture, CQRS, service meshes, orchestration layers, domain boundaries, and abstractions on top of abstractions on top of abstractions. Entire teams can spend months debating the “correct” architectural shape of a system that barely has users yet.&lt;/p&gt;

&lt;p&gt;And the strange part is that most of these conversations happen completely disconnected from the actual physical realities of software.&lt;/p&gt;

&lt;p&gt;Because underneath all the diagrams and patterns, software is still constrained by physics. Memory access still has a cost. Network calls still take time. Cache misses still exist. Serialization still burns CPU cycles. Distributed systems are still ultimately bounded by latency and coordination overhead. A request traveling across five services is still fundamentally slower than a function call inside a process, regardless of how elegant the architecture diagram looks in a presentation.&lt;/p&gt;

&lt;p&gt;Physics does not care about our abstractions.&lt;/p&gt;

&lt;p&gt;That is why I think there is a meaningful distinction between physics, engineering, and architecture in software, and I increasingly feel that the industry approaches them in the wrong order.&lt;/p&gt;

&lt;p&gt;Physics comes first. Physics defines the immutable constraints of the system. The things you do not negotiate with. The speed of light is not a suggestion. Memory bandwidth is not a suggestion. Disk throughput is not a suggestion. The behavior of queues under load is not a suggestion. Eventually every sufficiently scaled system crashes into these realities whether the engineers understand them or not.&lt;/p&gt;

&lt;p&gt;Then comes engineering. Engineering is where trade-offs happen. It is the art of navigating constraints while balancing reliability, simplicity, scalability, cost, operational burden, and business reality. Good engineering is deeply pragmatic. It is not ideological. A good engineer understands that sometimes duplication is cheaper than abstraction, that sometimes a monolith is the correct solution, and that operational simplicity is often more valuable than theoretical elegance.&lt;/p&gt;

&lt;p&gt;Only after those two layers comes architecture.&lt;/p&gt;

&lt;p&gt;Architecture is important, but architecture is downstream from reality. It is the organizational shape that emerges from constraints and trade-offs. The problem is that the software industry often treats architecture as if it were the foundation instead of the consequence.&lt;/p&gt;

&lt;p&gt;I also think part of the confusion comes from the word “architecture” itself.&lt;/p&gt;

&lt;p&gt;In traditional architecture, like civil architecture, the architecture is actually part of the final product. The shape of the building matters. The aesthetics matter. The structure itself creates emotional and experiential value. A beautiful building is, in many ways, the product.&lt;/p&gt;

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

&lt;p&gt;The user almost never experiences the architecture directly. Nobody opens an application and says, “Wow, this system uses beautifully designed bounded contexts.” Nobody pays more because your internal services follow clean architectural principles. The customer experiences latency, reliability, usability, speed, stability, and features. They experience outcomes.&lt;/p&gt;

&lt;p&gt;The architecture is mostly an internal implementation detail.&lt;/p&gt;

&lt;p&gt;And I think this subtly changes the incentives in a dangerous way. In civil architecture, investing heavily in architectural beauty can directly improve the value of the thing being built. In software, architectural beauty often becomes something engineers are mostly creating for themselves.&lt;/p&gt;

&lt;p&gt;That does not mean architecture is useless. Internal structure absolutely matters. A badly organized system becomes impossible to evolve. But software architecture should exist to serve engineering goals, not the other way around.&lt;/p&gt;

&lt;p&gt;A beautifully architected system that is slow, fragile, expensive, and operationally painful is still a bad product.&lt;/p&gt;

&lt;p&gt;Sometimes I feel like the industry treats software architecture almost like an art form, when in reality it should behave more like infrastructure engineering. The goal is not to produce the most intellectually elegant internal structure possible. The goal is to produce systems that survive contact with reality.&lt;/p&gt;

&lt;p&gt;And I think that inversion explains why so many modern systems feel overbuilt.&lt;/p&gt;

&lt;p&gt;A lot of developers learn architecture before they learn engineering. They learn patterns before they learn systems. They learn frameworks before they learn operating systems, networking, databases, memory behavior, or performance analysis. They become fluent in abstraction without becoming fluent in constraints.&lt;/p&gt;

&lt;p&gt;As a result, many systems are optimized for conceptual purity instead of practical effectiveness. They look beautiful in diagrams and become nightmares in production. Every layer introduces another retry policy, another queue, another serialization format, another network hop, another operational dependency, another failure mode. Complexity grows geometrically while the actual business problem often remains embarrassingly simple.&lt;/p&gt;

&lt;p&gt;At some point, especially at scale, software stops feeling like pure abstraction and starts feeling closer to applied physics. The engineers who build high-performance databases, trading systems, browsers, operating systems, rendering engines, or globally distributed infrastructure tend to understand this instinctively. They think in terms of latency, throughput, contention, scheduling, probability, cache locality, and resource allocation. They understand that every abstraction leaks eventually because underneath every abstraction there is still a machine doing work.&lt;/p&gt;

&lt;p&gt;And I think that mindset is becoming increasingly rare in mainstream software culture.&lt;/p&gt;

&lt;p&gt;We became extremely good at discussing the shape of systems while becoming strangely disconnected from the forces acting underneath them. The industry rewards architectural sophistication far more visibly than engineering restraint. Adding complexity often looks impressive. Removing complexity often looks invisible, even when it is the harder and more valuable work.&lt;/p&gt;

&lt;p&gt;But the longer I work on systems, the more I feel that the best engineering usually looks deceptively simple from the outside. Not because the problems are simple, but because the people building them deeply understand the underlying constraints well enough to avoid unnecessary complexity in the first place.&lt;/p&gt;

&lt;p&gt;Maybe that is the real difference.&lt;/p&gt;

&lt;p&gt;Physics defines the limits.&lt;br&gt;
Engineering navigates the trade-offs.&lt;br&gt;
Architecture is merely the shape left behind.&lt;/p&gt;

&lt;p&gt;Software is still bound by reality.&lt;/p&gt;

&lt;p&gt;Rodrigo Vidal&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwaredesign</category>
    </item>
  </channel>
</rss>
