Andrew Schwartz / 2026-07-10 / 7 min read
Software Is Easy in Isolation. Reality Is Hard.
The hard part of software is rarely defining what it should do. It is making it work in a world with users, networks, vendors, old data, and time.

Software can be surprisingly easy to understand when you isolate it.
You define the inputs. You define the rules. You define what should happen when each condition is met. Then you write code that turns those rules into behavior.
If X happens, do Y. If the user has this role, allow this action. If the value exceeds this threshold, trigger that process. If the API returns this response, update this record.
Inside a sufficiently controlled environment, software is almost comforting. The boundaries are known, the rules can be written down, and when the system misbehaves you can usually trace the problem until you find the mistake.
That is why prototypes feel easy. It is also why software becomes deceptively difficult.
The hard part is rarely defining what the software should do. The hard part is making it work in reality.
Reality does not respect our abstractions
A real system has users who misunderstand instructions, click buttons twice, leave tabs open for three days, lose their connection halfway through an operation, share accounts, enter information incorrectly, change their minds, and use the product in ways nobody imagined.
It has networks that fail. APIs that change. Vendors that go down. Data that arrives late. Data that contradicts other data. Records created under rules that no longer exist. Permissions inherited from a decision made five years ago by someone who has left. Processes that employees believe work one way and management believes work another.
And sometimes the business rule turns out not to be a rule at all.
Someone says: "When this happens, we always do this." So you build it.
Two weeks later: "Well, except when this customer does it." So you add the exception.
Then: "Unless it happens after 5 PM." Another exception.
Then: "Actually, accounting handles those differently at month end."
The code is not getting complicated because programming is hard. It is getting complicated because reality contains more state than the original model did. That distinction matters more than almost anything else in this work.
Isolation removes state
When we isolate a problem, we deliberately remove variables. That is not a cheat; it is one of the fundamental tools of engineering. A function has three inputs and one output. A test constructs exactly the state needed to prove that function behaves. Everything outside the boundary disappears, and reasoning becomes possible.
Production gets no such luxury. Production is where all of those isolated components meet each other.
The application meets the database. The database meets old data. The application meets another company's API. The API meets the network. The network meets somebody's phone. The phone meets a human. The human meets an organizational process. The process meets a regulation. The regulation meets an exception.
Something perfectly understandable on its own becomes part of an enormous state machine nobody completely controls. That is where the real work of software engineering starts.
The happy path is usually the easy part
Most software starts with a story. A customer submits an order. The system validates it. Payment is collected. The order is saved. The warehouse receives it. The customer gets a confirmation.
That flow is often straightforward to implement. Production asks different questions.
What happens if payment succeeds but saving the order fails? If the order saves but the confirmation never sends? If the payment provider times out after it actually processed the charge? If the user clicks Pay again? If the warehouse system is unavailable, or receives the request twice? If the customer changes the order while one of those operations is still in flight?
The rule was simple: customer pays, create order. The real system requires deciding what "paid", "created", "failed", "pending", and even "the same order" mean across several systems and several moments in time.
The difficulty is not the syntax. The difficulty is preserving truth.
Time makes it harder
Isolation removes another variable that matters enormously: time.
Code can be perfectly correct at one instant and still participate in a broken system. Two processes update the same record at once. A request is retried. A message arrives out of order. A job runs an hour later carrying assumptions that were true when it was queued. A user is looking at a screen that was accurate thirty seconds ago.
Distributed systems are especially unforgiving because there may be no single authoritative "now". Once time enters the picture, simple rules become questions about ordering, ownership, idempotency, recovery, and consistency.
None of that means the original rule was difficult. The environment around the rule became difficult.
Humans are part of the system
We also draw the boundary of software too tightly. A business application does not end at the edge of the computer. The person using it is part of the system, and so is the organization, and so are the incentives surrounding it.
You can build a technically excellent workflow that employees hate, and they will build a spreadsheet to route around it. You can build careful permissions that managers defeat by sharing a login. You can require beautifully structured data and then discover that the people entering it do not have the information at the moment your workflow demands it.
From the developer's chair, these look like people using the system incorrectly. From the system's perspective, they are simply more inputs. Good software requires understanding the environment the code will live in, not only the code.
Production teaches you different questions
There is a real difference between knowing how to build something and knowing how it behaves after thousands of people, machines, and edge cases have interacted with it for years.
Production stops you asking only does this work? and starts you asking:
- How does this fail?
- Can it recover on its own?
- What happens if it runs twice?
- How will we know it failed?
- Who owns the problem when it does?
- What are we assuming about another system?
- What happens when that assumption stops being true?
- Can a human understand what happened here six months from now?
Those questions are usually what separates software that is merely functional from software that is actually reliable.
Build for the world, not the model
There is a temptation to keep adding rules until reality fits the model. The better move is often the opposite: design knowing that reality is messy.
Allow uncertainty. Represent intermediate states instead of pretending everything is binary. Keep history. Make operations retryable. Make important actions observable. Expect external systems to fail and users to make mistakes. Create escape hatches for humans. Separate what you know from what you assume. Resist making an asynchronous process look synchronous because the interface would be tidier that way.
The code is the easy part
That sounds dismissive of programming. It is the opposite.
Programming is powerful precisely because we can take a messy piece of reality and build a small environment where the rules become deterministic. We create abstractions because the whole world is too complicated to hold at once. The danger is mistaking the abstraction for the world.
A clean architecture diagram makes a system look obvious. Boxes. Arrows. Services. Databases. Users. Every one of those arrows is hiding hundreds of assumptions.
The craft is deciding which complexity can safely be hidden and which has to be represented. That is why a system takes a weekend to prototype and years to mature. The prototype proves the idea works in isolation. Production proves whether it works in the world — and the world is where all the interesting problems are.
The full essay
Download the essay as a PDF if it is worth passing to someone who keeps being told the remaining work is "just wiring it up".
If you have a system that behaved perfectly in a demo and has been surprising you ever since, describe what is happening and we will tell you what it would take to make it dependable.



