Custom software design is routinely mistaken for the pictures of the screens. The screens are the last part, and the cheapest to change. The design decisions that matter are the ones that become expensive the moment code exists on top of them.
Design is mostly deciding what the system refuses to do
A system that permits everything is a system that enforces nothing, and every rule it declines to enforce becomes a rule a human has to remember. That is where errors live.
So the early conversation is less “what should it do” and more “what should be impossible”. Can an order ship before payment clears? Can a record exist without an owner? Can two people edit the same thing at once, and if so, who wins?
These sound like edge cases. They are actually the shape of the system, and they are almost impossible to retrofit, because by the time you want to enforce a rule there is already data in the database that violates it.
Model the states before the screens
Most business software is a state machine wearing a user interface. A thing exists, moves through a sequence of conditions, and eventually reaches a terminal state.
Getting that model right early is worth more than any amount of interface polish. The questions are unglamorous:
- What states can this record be in, named precisely?
- Which transitions are legal, and who is allowed to make them?
- Which transitions are reversible, and what happens to the history when they reverse?
- What is the terminal state, and can anything escape it?
Teams that skip this end up with a status field that accumulates values for three years until nobody can say what “pending review (old)” means or whether anything still reads it.
Find the exceptions before you commit
Every process description you are given is the clean version. The real process has exceptions, and the exceptions are where the cost hides.
The reliable way to surface them is not a workshop. It is watching the work happen and asking, at each step, what people do when it does not go that way. You will hear about the spreadsheet someone maintains on the side, the approval that is routinely given verbally and recorded later, the customer type that gets handled differently because of a decision made in 2019.
None of this appears in a requirements document, and all of it determines whether the system gets used or quietly worked around.
Decide what happens when integrations fail
Anything that crosses a network boundary will be unavailable sometimes. Design has to answer what the system does then, and the answer differs per integration.
For a payment gateway, the answer is usually to block and retry, because proceeding without confirmation is unsafe. For an analytics call, the answer is to drop it silently, because nobody should be unable to work because a metric did not record. For a document service, the answer might be to queue and reconcile later.
If this is not decided during design, it gets decided by whoever writes that function on the day, and it will be inconsistent across the system.
Design for the second release
The first release is the one everybody plans. The second is where most of the value appears, because it is informed by contact with real users.
That means the first build should be deliberately easy to change in the places you expect to learn something, and can be rigid everywhere else. Knowing which is which is the actual skill. Over-engineering everything for flexibility costs as much as building the wrong thing, and takes longer.
What a good design phase produces
Not a document nobody reads. A useful design phase ends with:
- A named list of states and legal transitions for each core record.
- The rules the system will enforce, and explicitly, the ones it will not.
- A failure behaviour for every external dependency.
- The exceptions found by observation, with a decision on each: support it, refuse it, or handle it outside the system.
- A first release that is smaller than the original brief, with the reasoning attached.
That last point is the tell. A design phase that did not shrink the scope probably did not examine it.
This is how we approach custom software development — architecture first, and the difficult questions asked while they are still cheap. Tell us what you are building.
Common questions
What happens during a software design phase?
Deciding what the system will refuse to do, modelling the states each record can hold and which transitions are legal, finding the exceptions by observation, and defining failure behaviour for every external dependency.
Why model states before designing screens?
Because screens are cheap to change and the data model is not. Once records exist that violate a rule, enforcing that rule later requires a migration. Most business software is a state machine wearing a user interface.
How do we know the design phase worked?
It shrank the scope and produced at least one thing you did not want to hear. A design phase that left the original brief intact probably did not examine it.


