A residual design focuses on the present in a way that does not curtail the future.
An Agile design traditionally focuses on the present in a way that makes future manoeuvre difficult.
Residuality is the design discipline of creating a boundary so that an implementation does not exclude future options.
An Agile notification service
Agile acknowledges that we cannot know the future. We do not know what requirements there may be tomorrow. Therefore, Agile states that we only build today what we know we need today. This is under the accepted principle that we can always change what we have in the future.
On this basis, a notification system is designed and implemented:
- a single delivery service with one endpoint
- direct notification integration: wired straight into SendGrid for email notification
- email-focused payload: SendGrid JSON permeates the solution
- provider-specific error handling: SendGrid error codes become baked into logic
This works. You release it. Your customers love notifications in their email.
What the first implementation hardwires
These are the assumptions encoded into the architecture.
- There is a single communication channel for notification delivery based only on email
- It is assumed invocation of the service is synchronous REST
- Routing to the email channel is implicit and hardwired into the service
- There is no retry, no queueing, and no multi-stage delivery
- Email addresses are the universal notification identity
- The call site will always receive a fixed result shape such as a single boolean success flag
- The call site is exposed to provider‑specific error codes and messages
- The call site assumes the invocation contract will not evolve beyond the current shape
- The call site assumes notifications are email and adopts email semantics
Agile encourages minimalism and minimalism cuts off future change options.
Why SMS breaks the first design
Six months pass and enough customers request notification by SMS that this becomes an additional messaging requirement.
Adding SMS is possible. However, handling SMS is fundamentally different to email and this raises a number of challenges within the context of a pre-existing solution that supports only email.
Why SMS is different
When sending a message via SMS it may be hours before the initiating service is informed whether the message has been delivered or not (the recipient's phone may be off). Therefore, asynchrony is required.
The realities of using SMS
- SMS delivery is multi‑stage. It moves through 'accepted', 'dispatched', 'delivered', and 'failed' states rather than a single synchronous success Boolean
- SMS operational behaviour is queue‑driven, not synchronous. Messages are accepted into a queue, processed asynchronously, and resolved through later delivery receipts rather than immediate REST responses
- SMS providers return delivery receipts. They emit callbacks or status updates that report dispatch, delivery, or failure after the initial acceptance
- SMS failures require retry windows. Providers expect controlled retry behaviour with backoff, respecting carrier rules and avoiding duplicate messages
- SMS identity is numeric. The recipient is a phone number, which carries region, format, and validation rules that differ from email identity
- SMS payload is constrained. Only short text is allowed, with strict length limits and no subject, HTML, or rich formatting
- SMS routing rules differ by region and partner. Some carriers require specific sender IDs, formats, or compliance steps that vary across countries and partner integrations
A residual design
Residuality needs focus on two concepts: the boundary and boundary rules.
The boundary
By defining endpoints that are channel specific (notify/email) new
notification channels can easily be incorporated.
Changes are isolated to the boundary for the channel type. If the code for
email delivery is updated, this only affects calls to notify/email.
The boundary is shaped by future uncertainty, not present requirements.
Boundary rules
Because there is one channel-type per endpoint, payloads can be endpoint specific, keeping each one focused on what is needed for just that channel.
Internal details of the RESTful implementation should remain only on the service side. Endpoint input and result payloads must not expose provider-specific information.
A residual RESTful boundary isolates channel semantics: notify/email is
synchronous, whereas notify/sms is asynchronous.
Data that must be sent to the client should be expressed in terms of the notification service (e.g. error IDs). Error codes and other implementation details must not be shared.
Consequences
A residual design leads to an architecture that is more amenable to future change. Isolating concerns ensures issues about that channel are kept in one place.
Not leaking information keeps the client clean and ensures implementation details do not unnecessarily permeate the rest of the codebase. Changing one channel-type provider for another becomes much easier.
RESTful interface residual checklist
You can review your RESTful interfaces to test for manoeuvrability. Can you:
- change an input or result payload without affecting other code?
- add a semantically different endpoint without breaking or having to update the others?
- remove a channel provider (like SendGrid) without changing the boundary?
- add a new channel provider behind a pre-existing endpoint without modifying other endpoints?
- change delivery semantics, e.g., if SMS forced email to become asynchronous, the boundary is leaking semantics
- change error semantics without exposing provider codes
- introduce a new identity type (phone number, device token) without rewriting existing payloads
- add a new delivery stage (e.g., queued or throttled) without breaking existing clients
- add a new endpoint without having to revisit any others?
- update endpoints independently of one another?
- change the domain concepts surfaced at the boundary without breaking existing clients?
The last one is key. Residuality is about ensuring that the domain concepts surfaced at the boundary can evolve.
Email hardwires 'subject', 'body', recipient'. SMS hardwires 'text', 'number'.
With a residual design none of these should define the domain model.
The boundary must express notification only, not email, SMS, or other channel types. The channel types are implementation details to provide the domain concept of notification.
Conclusion
Residuality is not about information hiding or the separation of concerns,. This is just good design practise.
Residuality is about preventing early decisions from excluding future ones.
Residuality is about ensuring that the first concern does not define an architecture for all future concerns.
Read next: How to Evaluate the Output of an AI Chat Session How to not be misled by your AI's first output
Related Articles
These five articles have an AI focus but they reinforces structured thinking, predictable behaviour, and disciplined interfaces, which are all required for residual design.
- Designing Prompts for Modern AI Systems
- How AI Works
- How to Evaluate the Output of an AI Chat Session
- A Beginner's Guide to AI Chatbot Prompting
- 10 Everyday AI Workflows That Save Hours
If this was useful, you can get more pieces like it in the Phroneses newsletter.
Table of Contents
Further Reading
An Introduction to Residuality Theory - Barry O'Reilly - NDC, Oslo, 2023 - https://www.youtube.com/watch?v=0wcUG2EV-7E
The Philosophy of Architecture - Barry O'Reilly, NDC, Oslo 2024 - https://www.youtube.com/watch?v=H8ZOp8ayluU
Architecture as Science - Barry O'Reilly, NDC, Oslo 2025 - https://www.youtube.com/watch?v=hUNuHQdMG2Y
Residues: Time, Change, and Uncertainty in Software Architecture. (Apr 6 2025) - https://gotopia.tech/books/120/residues-time-change-and-uncertainty-in-software-architecture