Production Change Request
Understanding the Production Change Request
Until now, the system has evolved through:
initial requirements
↓
technical design
↓
implementation
↓
production readiness
↓
shipping
↓
production incident
↓
corrective improvement
The application is no longer greenfield.
We now have:
an existing production database
existing API clients
historical Orders
existing Flyway migrations
existing tests
existing Payment behavior
existing security rules
existing observability
existing production contracts
Now Product introduces a new requirement.
This time:
nothing is broken
The business simply needs the system to do something it could not do before.
The Production Change Request
Product and Support bring the following request:
Administrators need to be able to issue a full refund for a successfully paid Order through the existing Payment Provider. The Order must reflect the successful refund so Support and the Customer can see that the payment was refunded.
This capability was intentionally outside v1.
Our existing system supports:
UNPAID
↓ payment succeeds
PAID
and:
UNPAID
↓ cancellation
CANCELLED
But once an Order becomes:
PAID
our existing model treats it as terminal.
There is currently no:
refund workflow
refund API
refund persistence
refund provider operation
refund state
That must now change.
Why This Is a Good Production Change
Refunds were not forgotten accidentally.
They were deliberately excluded from the original v1 scope.
At that time:
refunds
were a non-goal.
That allowed us to keep the initial Order lifecycle simple:
UNPAID
├── PAID
└── CANCELLED
Now the business requirement has changed.
Therefore the engineering scope can change too.
This is different from speculative design.
Before:
Maybe we will need refunds someday.
Correct response:
Do not build them yet.
Now:
Support needs refunds in production.
Correct response:
Understand the requirement
and evolve the existing system safely.
Do Not Start by Adding REFUNDED
The first instinct might be:
public enum OrderStatus {
UNPAID,
PAID,
CANCELLED,
REFUNDED
}
That may ultimately be part of the design.
But it is too early.
A production change request should first answer:
What does "refund" actually mean?
Who can request it?
Which Orders are eligible?
Is it full or partial?
What happens to Inventory?
What happens if the provider fails?
What happens if the result is unknown?
Can the refund be retried?
How is history represented?
What do existing API clients see?
What happens to existing Orders?
Only then should we change the model.
Start With Product Intent
The business problem is:
A Customer has successfully paid.
Support determines that the payment
must be returned.
The current backend has no supported
way to perform that operation.
The desired outcome is:
administrator initiates refund
↓
existing Payment Provider processes it
↓
successful refund becomes durable system state
↓
Customer and Support can see
that the Order was refunded
That is the requirement we need to preserve.
First Scope Decision — Full Refund Only
For this change request, Product confirms:
v1 refund support is full-refund only.
If an Order total is:
€120.00
the supported refund is:
€120.00
Not:
€20.00
or:
one OrderItem only
or:
arbitrary refund amount
Partial refunds remain outside scope.
Why Clarifying Full vs Partial Matters
Without this clarification, "refund" hides major complexity.
Partial refunds require questions such as:
Can one Order be refunded multiple times?
Can total refunded amount exceed Order total?
Can individual OrderItems be refunded?
What if one item has quantity 5
and only quantity 2 is refunded?
How do we represent remaining refundable amount?
How are repeated partial refunds displayed?
Those are real requirements.
They are not needed for the current change.
Scope remains:
one successful full refund
for an eligible paid Order
Second Scope Decision — Administrator-Initiated
Customers do not directly refund their own Orders.
The operation is available to:
administrator / authorized support operation
The existing application already distinguishes:
Customer permissions
Administrator permissions
So the new capability should use the established authorization model.
We are not creating:
customer self-service refund
in this change.
Why Authorization Matters
Refunding money is a privileged financial operation.
The requirement is not:
any authenticated user can refund
an Order they can see
Instead:
authorized administrator
must initiate it.
Later implementation must therefore verify:
authentication
administrator authority
before executing the financial workflow.
Third Scope Decision — Only Successfully Paid Orders
The requirement applies to:
paid Orders
It makes no sense to refund an Order for which:
no successful payment occurred
Therefore:
UNPAID
→ not refundable
and:
CANCELLED
→ not refundable
The exact Domain/API error behavior will be designed later.
For now, the business rule is:
Only an Order with a successfully completed payment can become eligible for refund.
Refund Is Not Cancellation
This distinction is fundamental.
Existing cancellation means:
UNPAID
↓
CANCELLED
and restores previously consumed Inventory.
Refund means:
payment already succeeded
and now:
money is returned
These are different operations.
Do not implement refund as:
order.cancel()
The semantics are not the same.
Inventory Behavior
Product confirms another important rule:
A financial refund does not automatically restore Inventory.
Why?
Because this change request does not introduce:
returns
physical item receipt
warehouse restocking
fulfilment reversal
A Customer receiving money back does not necessarily mean the physical product has returned to sellable Inventory.
Therefore:
successful refund
does not imply:
Inventory.restore(...)
This keeps refund and return/warehouse behavior separate.
Why This Matters
Our existing cancellation flow restores Inventory because:
Order was unpaid
Inventory had been reserved/consumed
during Order creation
the Order will no longer proceed
A refund happens later:
Order was paid
financial transaction completed
Whether stock should return requires a separate physical-return requirement.
We do not invent that.
Returns Remain Out of Scope
The production request is:
refund payment
not:
manage returned goods
So we are not adding:
return request
return reasons
warehouse receipt
item inspection
restocking
damaged-item handling
These could become future requirements.
Not now.
Shipping and Fulfilment Remain Out of Scope
Likewise, refund support does not require us to suddenly introduce:
shipment
delivery
fulfilment status
tracking number
Our Order Management Backend still does not model those capabilities.
The requirement should change only what it actually needs.
Existing Payment Provider Must Perform the Refund
The business already uses:
External Payment Provider
for payment execution.
The refund must therefore go through the existing Payment integration boundary.
We are not:
manually changing database state
to simulate a refund.
The money actually needs to be returned through the provider.
This immediately tells us:
refund is an external side-effecting workflow
just like payment.
But Provider Refund Semantics Are Not Yet Known
This is an important discovery.
Our existing course defined provider behavior for:
payment execution
but it did not define:
refund API
refund idempotency behavior
refund reference format
refund timeout semantics
refund lookup/reconciliation
We must not invent those details silently.
Therefore the change request contains a real technical unknown:
Before implementing provider-facing refund execution, we must understand the existing Payment Provider's refund capabilities and failure semantics.
That may require an investigation/spike before implementation is considered ready.
This Is a Legitimate Investigation
Earlier we avoided fake spikes such as:
Investigate whether PostgreSQL supports transactions.
That would be artificial.
But here we genuinely do not yet know:
Does provider expose full refund?
What identifier does refund require?
Does it have provider-side idempotency?
Can refund outcome become unknown after timeout?
Can we query a refund afterwards?
What provider reference identifies the refund?
These questions affect correctness.
Investigation is justified.
Refund Failure Is Not One Thing
Product says:
refund should succeed or report failure safely
Engineering must recognize multiple technical outcomes.
A provider call may result in:
confirmed refund success
confirmed rejection/failure
timeout
connection failure
malformed provider response
unknown outcome
The same lesson we learned from Payment applies again:
A timeout does not prove the external side effect did not happen.
This will strongly influence design.
We Must Not Mark an Order Refunded on Request Start
Bad workflow:
admin clicks refund
↓
Order marked REFUNDED
↓
provider call
If the provider rejects the refund:
local system says refunded
money was not returned
Clearly incorrect.
But the reverse ordering also has risk.
We Must Not Assume Provider Success and Database Success Are Atomic
Possible:
Provider refund succeeds
↓
application fails before local state update
Now:
money returned
but local system may still appear:
PAID
This is exactly the kind of external side-effect consistency problem we already encountered with Payment.
A local PostgreSQL transaction cannot make:
Payment Provider
part of:
COMMIT / ROLLBACK
Existing Payment Lessons Matter
When we originally designed Payment, we learned:
do not hold a database transaction open
over a remote financial call
and:
remote success
cannot be rolled back
by PostgreSQL
Those principles still apply.
A new requirement does not invalidate established failure reasoning.
Module 14 should reuse what the system already knows.
Refund Retry Safety Matters
Imagine:
refund request sent
↓
provider processes refund
↓
response is lost
If we blindly call refund again:
could money be refunded twice?
The answer depends on provider semantics.
Therefore a safe refund workflow needs:
operation identity
duplicate-processing protection
unknown-outcome handling
But we should not choose the exact mechanism until provider capabilities are confirmed.
Existing Order History Must Remain Valid
We already have historical Orders in production.
A schema/domain change cannot assume:
all existing rows were created
with refund fields
Any migration must account for:
existing UNPAID Orders
existing PAID Orders
existing CANCELLED Orders
without corrupting them.
Existing Orders Must Still Be Readable
Suppose we add new refund-related persistence.
After deployment:
GET /api/v1/orders/{orderId}
must still work for Orders created:
before refund support existed
We cannot require historical data that never existed.
This is the core difference from greenfield modeling.
Existing API Clients Matter
Customers already consume:
Order responses
The current status contract contains:
UNPAID
PAID
CANCELLED
If we decide later to introduce:
REFUNDED
that is an API compatibility change.
Existing clients may contain code such as:
switch(status) {
UNPAID ...
PAID ...
CANCELLED ...
}
A new enum value may affect them.
Therefore:
add REFUNDED
cannot be treated as a trivial Java enum edit.
We must assess API compatibility deliberately.
Do We Definitely Need REFUNDED OrderStatus?
Not yet decided.
Possible designs include:
Option A
Order.status gains REFUNDED
or:
Option B
Order lifecycle remains focused on Order state,
while Payment/Refund state records the financial outcome
or some combination.
We will evaluate these in the design lesson.
Do not decide architecture while still interpreting the requirement.
Customer Must Be Able to See Refund Outcome
Product explicitly requires:
Customer and Support can see
that the payment was refunded.
Therefore the change affects some customer-facing read behavior.
We need eventually to determine:
Does Order response expose a refund state?
Does Order status itself change?
Does Payment information become visible?
What fields are needed?
Those are API design questions.
The requirement only says:
refund must be visible
not how.
Administrator Needs a Refund Operation
We also know we need a command surface.
Conceptually:
administrator
↓
request full refund
↓
backend
But we should not yet assume the endpoint.
Possibilities might include:
POST /api/v1/orders/{orderId}/refund
or another resource-oriented representation.
Exact API shape belongs to later design.
The requirement is:
administrator can initiate
full refund for eligible Order
Refund Is Not a Generic Payment CRUD Operation
Avoid jumping to:
/api/v1/refunds
POST /payments
PATCH /payment
without understanding resource/workflow semantics.
Our existing API favors explicit business operations such as:
/orders/{orderId}/pay
/orders/{orderId}/cancel
The new endpoint should eventually fit established conventions unless a good reason exists not to.
Existing Order Ownership Still Matters
Customer Order reads already enforce:
Customer sees only own Orders
Refund does not change that.
Administrator refund authority and Customer read ownership are separate security rules.
We must preserve both.
Historical Price Still Matters
Refund amount should be based on:
what was actually paid / historical Order amount
not:
current Product prices
Suppose:
Order O1:
unitPriceCents = 5000
Later Product price becomes:
7000
Refunding O1 must not suddenly return:
7000
because current catalog price changed.
Our historical pricing model becomes useful again.
But Order Total Alone May Not Be Enough
Payment consistency matters.
Ideally the refund corresponds to:
the successful Payment
not merely:
recalculate something from current Product state
The exact provider/reference relationship will be assessed once we inspect the existing Payment persistence and provider contract.
Again:
understand before changing
One Successful Full Refund Only
For this course scenario, Product confirms:
A successfully refunded Order cannot be refunded again.
So:
PAID
→ full refund succeeds
→ no second full refund
Retry safety still matters because:
same refund operation
may need to be retried after an uncertain provider outcome.
But a separate new attempt must not intentionally refund the same payment twice.
What Happens After a Confirmed Refund?
At product level we know:
the Order must visibly reflect
that its payment was refunded
We have not yet decided whether that means:
Order.status = REFUNDED
or:
Payment/Refund state = REFUNDED
That is a design decision.
Do not confuse:
required observable behavior
with:
implementation representation
Failed Refund
If the provider gives a confirmed failure:
money was not refunded
Therefore the Order must not be represented as successfully refunded.
The system should make the failure visible to the administrator through a safe API response.
Exact error codes depend on final design/provider semantics.
Unknown Refund Outcome
This is more dangerous.
Suppose:
request sent to provider
provider may have processed it
connection times out
We cannot safely conclude:
refund failed
and blindly retry.
Nor can we safely conclude:
refund succeeded
without confirmation.
Therefore:
unknown refund outcome
is a real state the design may need to represent or recover from.
This is a technical impact we already know before choosing implementation.
We Need Provider Capability Investigation
The provider investigation should answer:
1. How is a full refund initiated?
2. Which successful payment identifier is required?
3. Does the provider accept an idempotency key
or merchant refund reference?
4. What does a confirmed successful response look like?
5. What does a confirmed rejected response look like?
6. What happens on duplicate refund requests?
7. Can refund status be queried later?
8. Can timeout leave outcome unknown?
9. Does the provider expose a stable Refund ID?
10. Are there provider states between
requested and completed?
Implementation should remain blocked until the essential answers are known.
This Does Not Mean We Stop All Analysis
Even without provider details, we already know which system areas may be affected.
We can begin an impact map.
Preliminary Impact Map
Domain
Potentially affected:
Order lifecycle
financial state representation
Questions:
Does REFUNDED belong to OrderStatus?
Is refund financial state separate from Order state?
What constitutes refund eligibility?
Application
New workflow required:
RefundOrderUseCase
or equivalent application operation.
Responsibilities likely include:
load Order
verify eligibility
coordinate durable refund attempt state
call provider outside DB transaction
apply confirmed outcome safely
Exact design comes later.
Payment Integration
Definitely affected.
Current external provider boundary supports:
payment
The new requirement requires:
refund
We must determine whether to:
extend existing PaymentService boundary
or introduce another provider-neutral capability.
Not decided yet.
Persistence
Almost certainly affected.
Why?
We need durable evidence of:
refund operation
provider result
relationship to Payment/Order
especially under:
timeout
restart
retry
local update failure
But the exact schema is a design decision.
API
Affected in at least two directions:
administrator command
and:
customer/support visibility
Compatibility with existing Order responses must be assessed.
Security
Affected.
Need:
administrator authority
for refund execution.
Customer ownership remains required for Customer reads.
Testing
Affected extensively.
We will need tests for:
eligible paid Order
unpaid Order
cancelled Order
already refunded Order
provider success
provider rejection
provider timeout
unknown outcome
duplicate/refund retry
authorization
local persistence failure after provider success
Exact scenarios will depend on final design.
Observability
Likely affected.
Financial operations need useful signals such as:
refund requested
refund succeeded
refund confirmed failed
refund outcome unknown
refund reconciliation
But exact metric/log vocabulary comes after state semantics are defined.
Inventory
For the confirmed scope:
Inventory workflow does not change
because refunds do not automatically represent returned stock.
This is an important impact conclusion.
Not every adjacent capability must change.
Product
Also currently:
no Product Domain change required
Refund amount must use historical Order/payment meaning.
Current Product price or active status should not control refund eligibility.
This preserves the historical boundary we intentionally designed earlier.
Product Deactivation Must Not Block Refund
Suppose:
Customer buys Product P1
Order becomes PAID
Admin later deactivates P1
Support then needs to refund the Order
Should Product active state block refund?
No.
The refund concerns:
existing historical paid Order
not whether P1 is currently orderable.
This mirrors our earlier rule that Product deactivation does not rewrite existing Order history.
Current Product Price Must Not Change Refund Amount
Likewise:
Product price today
has no authority over:
historical paid Order
Refund logic must not load current Product price and calculate from it.
The relevant financial truth comes from the existing Order/payment history.
Existing Paid Orders Need to Become Eligible
The feature is not only for Orders created after deployment.
A major production question is:
Can a paid Order that existed before refund support be refunded after the feature launches?
Product says:
Yes.
That has major migration implications.
We cannot design a feature that only works for:
new Orders
if Support needs to refund historical paid Orders.
Historical Compatibility Becomes a Hard Requirement
Therefore whatever refund persistence we introduce must allow:
existing PAID Order
+
existing successful Payment information
to enter the new refund workflow.
We cannot require new data that older successfully paid Orders could never possess unless we can derive or migrate it safely.
This will be a central design question.
Existing Cancelled and Unpaid Orders Remain Unchanged
Historical:
UNPAID
Orders remain unpaid.
Historical:
CANCELLED
Orders remain cancelled.
We do not run a broad migration that changes old Order lifecycle state merely because refunds were added.
API Backward Compatibility
Existing clients may not immediately understand new refund information.
We will need to assess:
Can we add an optional field safely?
Will we introduce a new OrderStatus value?
Do clients treat unknown enum values safely?
Does OpenAPI change?
Does frontend require coordinated release?
This is why:
existing API clients
matter in Module 14.
Database Compatibility
Existing deployment also means:
old application version
and:
new schema
may briefly coexist during rollout depending on deployment strategy.
A good migration should preferably be:
additive
where possible.
We should avoid immediately:
dropping columns
rewriting all historical rows
making old application versions unable to start
without a deployment reason.
Exact migration comes later.
Definition of Done Still Applies
This production change is not Done merely when:
provider returns refund success
Our existing Definition of Done still requires, where relevant:
agreed scope implemented
acceptance criteria satisfied
failure behavior handled
tests added
existing tests pass
build succeeds
migration included
API documentation updated
technical documentation updated
PR reviewed
CI passes
Production changes do not get a lower quality bar because they arrive later.
Refine the Product Requirement
We can now rewrite the initial request into clearer behavior.
Production Change Request — Full Order Refunds
Goal
Authorized administrators can issue a full financial refund for an eligible paid Order through the existing Payment Provider.
Required Behavior
Only successfully paid Orders
are eligible for refund.
Refund amount represents
the full successful Order/payment amount.
A successful refund becomes
durable system state.
The Customer and Support can see
that the payment was refunded.
The same successful payment
must not be refunded twice.
Provider failure must not
incorrectly mark the Order refunded.
Unknown provider outcome must be
handled safely rather than blindly retried.
Existing historical paid Orders
must be eligible where required
Payment information exists.
Authorization Requirement
Refund initiation requires
administrator authority.
No customer self-service refund endpoint is part of this change.
Inventory Requirement
Refund does not automatically
restore Inventory.
Returns/restocking remain separate future concerns.
Scope
Included:
full refund
paid Orders
administrator initiation
existing Payment Provider
durable refund outcome
safe provider failure handling
safe retry/duplicate protection
historical paid Orders
customer/support visibility
Explicit Non-Goals
Still out of scope:
partial refunds
item-level refunds
returns
restocking
shipping
fulfilment
refund to a different payment method
store credit
coupons
discount changes
multiple Payment Providers
automatic refund on cancellation
Customer self-service refund
This prevents the production request from expanding into an entire commerce platform.
Open Technical Questions
Before implementation we still need answers to:
1. How should refund state be represented?
2. Does OrderStatus change?
3. What durable refund-attempt model is needed?
4. What Payment Provider refund capabilities exist?
5. What identifies the original provider Payment?
6. How does provider idempotency work?
7. How do we recover unknown outcomes?
8. What API exposes refund initiation?
9. How is refund status exposed to Customers?
10. How do we preserve compatibility
with existing API clients?
11. What migration supports historical paid Orders?
12. What concurrency rules prevent
two administrators refunding simultaneously?
Those are design questions, not reasons to expand the product requirement further.
We Have Not Designed the Solution Yet
At this stage, do not create:
REFUNDED enum
refunds table
RefundService
POST /refund
provider DTOs
yet.
We first needed a bounded, explicit requirement.
That is now complete.
Existing System Context
Our production baseline before this change is:
Order statuses:
UNPAID
PAID
CANCELLED
Payment:
external provider
durable PaymentAttempt coordination
unknown-outcome recovery
Cancellation:
UNPAID only
restores Inventory
Create Order:
idempotent through Customer-scoped
Idempotency-Key
Architecture:
modular monolith
Handler → UseCase → Repository
Persistence:
PostgreSQL + Flyway
The refund feature must fit this system.
We are not designing from a blank page.
Preliminary Risk Register
Even before detailed design, the requirement exposes several risks.
Risk 1 — Double Refund
two administrators
or retry
or timeout
↓
provider receives refund twice
We must prevent unintended duplicate financial effects.
Risk 2 — Provider Success, Local Failure
provider returns/succeeds with refund
↓
database update fails
Money has moved externally while local state may remain stale.
This is a major consistency risk.
Risk 3 — Unknown Outcome
refund call sent
↓
timeout
We may not know whether the provider refunded.
Blind retry may be dangerous.
Risk 4 — Concurrent Refund Requests
Two authorized users could issue:
refund O1
at nearly the same time.
Application/database coordination must ensure one logical refund workflow wins.
Risk 5 — Existing API Compatibility
If we change:
OrderStatus
existing clients may not recognize the new value.
The API evolution needs deliberate review.
Risk 6 — Historical Orders
Older paid Orders may lack data introduced by new refund persistence.
The feature must not silently make historical Orders impossible to refund if Product requires them to remain eligible.
Risk 7 — Incorrect Inventory Restoration
A developer might assume:
refund
=
cancellation
and restore Inventory.
That would violate the new requirement because return/restocking is not part of this feature.
What Has Not Changed
Even after the product request, these remain true:
one modular monolith
PostgreSQL transactional persistence
Handler → UseCase → Repository
external Payment Provider boundary
Customer ownership rules
administrator authorization
historical unitPriceCents
money stored as long cents
UUID resource identities
RFC 7807 API errors
Testcontainers for PostgreSQL behavior
A change request should change only what the new requirement demands.
Product Requirement vs Technical Design
We now have:
Product requirement:
support safe full refunds
We do not yet have:
Technical design:
how to represent and execute refunds
That distinction matters.
We will next examine the existing code and architecture boundaries and ask:
Which responsibilities actually change?
Only after that should we decide the implementation shape.
Engineering Principle
The first principle:
A production change request must be interpreted in the context of the system that already exists. Existing data, API clients, tests, production behavior, and external integrations are part of the requirements now.
The second:
A feature that was correctly excluded by YAGNI can later become a legitimate requirement. Good earlier boundaries should make that evolution manageable without pretending the old system was designed for every future feature.
The third:
Separate product semantics from technical representation. "A paid Order can be fully refunded" is a requirement;
REFUNDED, arefundstable, and a particular endpoint are design decisions that still need evaluation.
And:
When new financial behavior touches an external provider, uncertain outcomes, retries, and duplicate side effects must be considered before implementation—not after production teaches the same lesson again.
Summary
In this lesson, we established the Module 14 production change request:
Authorized administrators must be able to issue a full refund for an eligible paid Order through the existing Payment Provider, and the successful refund must become visible durable system state.
We clarified that:
- Refunds were intentionally outside v1 and are now a real new requirement.
- This is system evolution, not correction of an original scope mistake.
- Full refunds are supported; partial refunds are not.
- Refunds are administrator-initiated.
- Customer self-service refund is out of scope.
- Only successfully paid Orders are eligible.
- Refund and cancellation are different operations.
- Refund does not automatically restore Inventory.
- Returns and restocking remain out of scope.
- Shipping and fulfilment remain out of scope.
- The existing Payment Provider must perform the financial refund.
- Provider refund semantics are currently a genuine technical unknown.
- We must investigate provider refund/idempotency/reconciliation capabilities before provider-facing implementation is ready.
- A provider timeout cannot automatically be treated as confirmed refund failure.
- PostgreSQL cannot atomically roll back an external refund.
- Duplicate refund protection is required.
- Concurrent refund requests must be considered.
- Existing historical paid Orders need to remain eligible.
- Existing UNPAID and CANCELLED Orders remain unchanged.
- Current Product active state must not determine refund eligibility.
- Current Product price must not determine historical refund amount.
- Existing Order history and Payment history are authoritative.
- Customer and Support must be able to observe successful refund outcome.
- How that outcome is represented remains a design question.
- We have not yet decided whether
OrderStatusgainsREFUNDED. - Existing API clients make enum/API evolution a compatibility concern.
- Persistence will likely change, but the schema is not yet designed.
- Security must enforce administrator authority.
- Inventory does not need a new refund mutation.
- Product does not need new refund behavior.
- The feature must preserve the established architecture rather than redesign the system from scratch.
- Partial refunds, returns, restocking, shipping, fulfilment, store credit, and multiple providers remain non-goals.
- The most important risks are double refund, provider-success/local-failure, unknown provider outcome, concurrent refunds, API compatibility, historical-data compatibility, and accidental Inventory restoration.
Next lesson:
Assessing the Impact on the Existing System
We will take the confirmed requirement and systematically inspect:
Order Domain
Payment workflow
PaymentAttempt model
API contracts
PostgreSQL schema
existing migrations
security
historical Orders
external provider boundary
tests
observability
deployment compatibility
and classify each area as:
must change
may change
must not change
before writing any refund implementation.
