Joining the Backend Team
Creating Our Initial Engineering Backlog
So far, we have done a significant amount of engineering work without writing production code.
We have:
- understood the Product requirement
- identified ambiguity
- separated functional and non-functional requirements
- written User Stories and Acceptance Criteria
- established the engineering context
- defined our Definition of Done
Our next responsibility is:
Break the larger Product requirement into engineering work that an engineer can realistically pick up, implement, test, review, and complete.
In this lesson, we will create the first engineering backlog for our Order Management Backend.
This will not be just a list of tasks.
We will think about:
- which work should happen first
- which tasks depend on other tasks
- how large a ticket should be
- how Product Stories differ from engineering tickets
- how foundational work and feature work should be balanced
- how to keep unnecessary scope out of the backlog
- how to preserve traceability from requirement to implementation
By the end of the lesson, we will have a realistic initial backlog.
After that, we can move into technical design.
What Is a Backlog?
A backlog is an ordered collection of planned work.
A software team's backlog may contain:
features
bugs
technical work
documentation
operational work
investigations
Our application is new, so the initial backlog will contain mostly:
foundation
+
first Product capabilities
But a backlog should not simply be:
things we might build someday
A useful backlog contains work that is:
- understood enough
- scoped enough
- prioritized enough
- actionable enough
for an engineer to begin meaningful work.
Product Backlog vs Engineering Backlog
We already have Product-level Stories:
Browse Available Products
Create an Order
View Order History
Cancel an Eligible Order
Pay for an Order
Manage Products
Manage Inventory
These describe user value.
But they are still too broad to function directly as engineering implementation tickets.
For example:
Create an Order
eventually involves several concerns:
Domain model
Persistence
Product validation
Inventory handling
Price calculation
Transactions
API
Validation
Tests
The Product Story expresses what the user needs.
The engineering backlog breaks that need into implementable changes.
A Ticket Should Represent a Coherent Change
A good ticket should ideally be:
understandable
bounded
reviewable
testable
Bad:
BACKEND-100
Build backend.
That is a project goal, not an engineering ticket.
Another bad example:
BACKEND-101
Implement Orders, Payments, Authentication,
Inventory, Tests, and Deployment.
That contains far too much scope.
Tickets like this are difficult to:
- review
- estimate
- parallelize
- test
- recover from when something goes wrong
- reason about in terms of progress
Tickets Can Also Be Too Small
The opposite extreme is equally unhelpful.
For example:
BACKEND-201
Create Product.java.
BACKEND-202
Add name field.
BACKEND-203
Add price field.
BACKEND-204
Add getter.
At that point, the process overhead becomes larger than the engineering value of the ticket.
A ticket should represent a meaningful engineering outcome.
Prefer Outcome-Oriented Tickets
Compare:
Weak:
Add JPA.
Better:
Persist Products in PostgreSQL.
Weak:
Create Handler.
Better:
Expose Product creation through the administration API.
Weak:
Add tests.
Better:
Add integration coverage for Product persistence and validation.
Technology may appear inside the implementation.
The ticket title should usually describe the engineering result.
Ticket Structure
For the course project, we will use a lightweight format:
# BACKEND-XXX — Ticket Title
## Context
Why does this work exist?
## Scope
What should be implemented?
## Acceptance Criteria
What behaviour must be true?
## Out of Scope
What should not be added as part of this ticket?
## Dependencies
What must already exist before this work can be completed?
Not every ticket needs every section.
But this structure is useful when the work has meaningful scope or dependencies.
Ticket IDs
We will use identifiers such as:
BACKEND-101
BACKEND-102
BACKEND-103
These IDs do not require any particular project-management tool.
A real team might use:
Jira
Linear
GitHub Issues
another issue tracker
The ID simply gives us a stable reference.
For example:
This Pull Request implements BACKEND-104.
Start With Dependencies, Not Only Product Priority
Suppose the highest-value Product capability is:
Create an Order
That does not necessarily make it the first implementation ticket.
Order creation needs other concepts to exist first:
running application
database foundation
Products
Inventory
authenticated Customer identity
The dependency structure matters.
Conceptually:
Application Foundation
↓
Persistence Foundation
↓
Products
↓
Inventory
↓
Orders
↓
Payments
Authenticated identity is a cross-cutting prerequisite for protected Customer and Administrator operations, but we are not building a local Customer-management subsystem.
Some work can happen in parallel.
Still, dependencies influence backlog ordering.
First, We Need a Running Application
Our first technical milestone is:
A clean Spring Boot repository that builds and starts successfully.
So our first ticket is:
BACKEND-101
Bootstrap Order Management Backend
This is not a business feature.
It is the foundation for every business feature that follows.
BACKEND-101 — Bootstrap Order Management Backend
Context
We need a working Spring Boot application before feature implementation can begin.
Scope
Create the Gradle project.
Configure Spring Boot.
Create the application entry point.
Add baseline project configuration.
Verify the application builds and starts locally.
Acceptance Criteria
The project builds successfully with Gradle.
The Spring Boot application starts successfully.
The repository contains the agreed baseline structure.
A clean checkout can be built without IDE-specific setup.
Out of Scope
Product APIs
Database schema
Authentication
Order workflows
Payment integration
Production Dockerfile
We deliberately do not place half of the course inside the bootstrap ticket.
Do Not Create the Entire Package Structure Up Front
We could create empty packages immediately for:
product
inventory
order
payment
and many speculative subpackages.
But empty future-looking structure often creates architecture before we actually understand the need.
A better approach is:
Let structure emerge as real capabilities are implemented.
We can have an architectural direction without generating dozens of unused classes and interfaces.
Database Foundation
Products, Inventory, and Orders require durable persistence.
So we need an early database foundation ticket:
BACKEND-102
Configure PostgreSQL and Flyway
BACKEND-102 — Configure PostgreSQL and Flyway
Context
Transactional application data will be stored in PostgreSQL.
Schema changes must be versioned using Flyway.
Scope
Configure PostgreSQL connectivity.
Add required persistence dependencies.
Configure Flyway.
Verify migrations run correctly.
Provide a repeatable local-development PostgreSQL setup.
Establish isolated PostgreSQL support for integration testing.
Acceptance Criteria
The application can connect to PostgreSQL.
Flyway is enabled.
A migration can be applied successfully.
Local development can start the required database consistently.
Integration tests do not depend on a developer
manually preparing a local database.
Out of Scope
Product schema
Inventory schema
Order schema
Production database provisioning
For local development, Docker Compose may be used for supporting infrastructure such as PostgreSQL.
The Spring application itself can continue running through Gradle or the IDE until we reach the containerization module.
Product Capability Comes Before Orders
Orders reference Products.
So Product is our first meaningful business capability.
An initial breakdown could be:
BACKEND-103
Model the Product Domain
BACKEND-104
Persist Products
BACKEND-105
Implement Product Administration
BACKEND-106
Expose Available Product Browsing
BACKEND-103 — Model the Product Domain
Context
Before Inventory and Orders can use Product information, we need to define the Product concept required by v1.
Scope
The Product model should represent only the information currently required by the ordering flow.
Conceptually, the Product needs:
stable identity
name
price
active/inactive state
The exact Java representation will be decided during technical design.
Acceptance Criteria
A Product has a stable identity.
A Product has the information required
by the current ordering flow.
A Product can be active or inactive.
Invalid Product state is prevented where appropriate.
Domain behaviour has focused tests.
Out of Scope
Inventory quantity
Categories
Images
Variants
Discounts
Physical deletion
We do not add generic ecommerce features simply because they might exist in another commerce system.
BACKEND-104 — Persist Products
Context
Product data must survive application restarts.
Scope
Create the required Product database migration.
Map Product persistence.
Provide Repository operations required
by current Product UseCases.
Acceptance Criteria
Products can be stored in PostgreSQL.
Products can be retrieved by identifier.
Product active/inactive state is persisted.
Product price is persisted using
the agreed server-side monetary representation.
Schema creation is managed through Flyway.
Persistence behaviour is covered by integration tests.
Dependencies
BACKEND-102
BACKEND-103
BACKEND-105 — Implement Product Administration
The Product Management Story includes several Administrator operations.
Instead of forcing all operations into a single massive implementation, this area may later be refined into smaller tickets.
At backlog level, the expected capability includes:
create Product
view Product
update Product
deactivate Product
Physical deletion is not required.
Product Creation as the First Vertical Slice
One early implementation ticket may be:
BACKEND-105A
Expose Product Creation
Context
Administrators need to add Products to the catalog.
Scope
Accept Product creation input.
Validate supported fields.
Execute CreateProductUseCase.
Persist the Product.
Return the created Product representation.
Acceptance Criteria
A valid Product can be created.
Invalid required input is rejected.
Price cannot be negative.
The created Product is persisted.
A newly created Product is active.
Product creation does not automatically create Inventory.
That final point matters.
Product existence and Inventory availability are separate concepts.
Product Update and Deactivation
Later Product administration tickets can cover:
Update Product
Deactivate Product
The important business rule is that deactivation prevents future ordering but does not rewrite historical Orders.
We still do not introduce:
DELETE Product
for v1.
Product Browsing Depends on Inventory
An important refinement is that a Customer-facing Product is not orderable merely because:
active = true
For v1, Product browsing eventually needs both:
Product active
AND
Inventory available_quantity > 0
So Product browsing depends on the Inventory capability as well.
That affects backlog dependencies.
Inventory Is Separate From Product
Product and Inventory are related but represent different responsibilities.
Conceptually:
Product
identity
name
price
active state
Inventory
available quantity
Inventory changes frequently for operational reasons.
Product metadata changes for different reasons.
Keeping them separate makes the model clearer.
Our initial Inventory backlog:
BACKEND-106
Model and Persist Inventory
BACKEND-107
Implement Inventory Administration
We can move Customer-facing Product browsing after those foundations exist.
BACKEND-106 — Model and Persist Inventory
Context
Orderability depends on available Inventory.
Scope
Represent Inventory for a Product.
Persist available quantity.
Prevent invalid negative quantity.
Acceptance Criteria
Inventory is identified by Product identity.
Available quantity can be stored and retrieved.
Available quantity cannot be negative.
Inventory schema is managed through Flyway.
Persistence behaviour is integration-tested.
Out of Scope
Multiple warehouses
Backorders
Reservation subsystem
Inventory movement ledger
Audit history
Inventory does not need a separate InventoryId.
The Product identity is sufficient for the Inventory record.
BACKEND-107 — Implement Inventory Administration
Context
Administrators need to control the current available quantity.
Scope
Expose current Inventory.
Allow an Administrator to set
the exact available quantity.
Reject invalid negative values.
Acceptance Criteria
Current Inventory can be viewed.
Available quantity can be set
to a non-negative whole number.
Setting quantity to the same value
is safe and has the same resulting state.
Inventory cannot become negative
through the administrative operation.
Out of Scope
Inventory delta commands
Stock ledger
Warehouse transfers
Automatic replenishment
For v1, the administrative operation sets the authoritative current quantity.
BACKEND-108 — Expose Available Product Browsing
Context
Customers need to see Products they can actually order.
Scope
Expose bounded Product browsing.
Return Product information required
to make an ordering decision.
Filter based on Product and Inventory state.
Acceptance Criteria
Only active Products are returned.
Only Products with available Inventory
greater than zero are returned.
A Product without an Inventory record
is not presented as orderable.
Product price is included.
Results are bounded.
Ordering is deterministic.
Dependencies
BACKEND-104
BACKEND-106
The concrete pagination contract will be defined during API design.
Customer Identity Is Not a Local Subsystem
Orders must belong to a Customer.
But we already established:
Authentication is provided by an existing identity capability.
Therefore, we do not create tickets for:
Customer table
Customer registration
Customer persistence
Password management
Customer creation API
What we need inside application workflows is much smaller:
a stable authenticated Customer identity
BACKEND-109 — Represent Customer Identity for Application Workflows
Context
Order workflows need to associate Orders with the authenticated Customer without owning identity management.
Scope
Represent the stable Customer identifier used by application UseCases.
Acceptance Criteria
The application can represent
a stable Customer identifier.
Order workflows can associate data
with that Customer identity.
The representation does not assume
that Customer identity is locally managed.
No password, registration, or account
lifecycle responsibilities are introduced.
Out of Scope
Customer table
Registration
Password storage
Password reset
Email verification
Identity provider implementation
The actual authenticated identity will later be supplied through Spring Security.
Until then, UseCases can receive an already-resolved CustomerId.
Order Domain Comes Next
Once Product, Inventory, and Customer identity concepts are available, we can model Orders.
Order-related backlog:
BACKEND-110
Model Order and OrderItem Domain
BACKEND-111
Persist Orders and OrderItems
BACKEND-112
Implement Order Creation Workflow
BACKEND-113
Expose Order Creation API
BACKEND-114
Expose Customer Order History and Detail
BACKEND-115
Implement Order Cancellation
BACKEND-110 — Model Order and OrderItem Domain
Context
Order creation, history, Payment, and cancellation all depend on the Order lifecycle.
Scope
Model:
Order
OrderItem
Customer ownership
purchase-time pricing
Order lifecycle
For v1, the lifecycle is intentionally small:
UNPAID
PAID
CANCELLED
Conceptually:
UNPAID
├──→ PAID
└──→ CANCELLED
PAID and CANCELLED are terminal in v1.
Acceptance Criteria
An Order belongs to one Customer.
An Order contains at least one OrderItem.
An OrderItem contains Product identity,
quantity, and purchase-time unit price.
Quantity must be positive.
The Order total can be derived from its items.
A new Order starts as UNPAID.
Only valid state transitions are allowed.
PAID and CANCELLED Orders do not transition
back into an active state.
Out of Scope
Shipping states
Processing state
Refund state
Fulfilment workflow
Returns
Transition history
We model only the lifecycle our current Product requirements actually need.
BACKEND-111 — Persist Orders and OrderItems
Context
Orders must remain available for history, Payment, and cancellation.
Scope
Create Order-related Flyway migrations.
Persist Orders.
Persist OrderItems.
Persist Customer ownership.
Preserve purchase-time pricing.
Persist Order status and creation time.
Acceptance Criteria
Orders can be stored with Customer identity.
OrderItems persist Product identity,
quantity, and historical unit price.
Order status is persisted.
Order creation time is persisted.
Persisted Orders can be reconstructed
with data required by application UseCases.
Database constraints protect relevant
data integrity.
Persistence behaviour is integration-tested.
Dependencies
BACKEND-102
BACKEND-110
No Customer table dependency exists.
The Order stores the stable Customer identifier directly.
BACKEND-112 — Implement Order Creation Workflow
This is the first major business workflow.
Context
Customers need to create valid Orders without violating Product, pricing, or Inventory rules.
Scope
The UseCase must:
validate requested items
verify Products exist
verify Products are active
validate quantities
reject duplicate Product lines
consume available Inventory safely
determine authoritative Product prices
capture purchase-time unit prices
create the Order
persist the complete result atomically
Acceptance Criteria
An Order contains at least one item.
Every referenced Product exists.
A Product appears at most once
in the request.
Quantity is a positive whole number.
Inactive Products cannot be ordered.
Requested quantity must be available.
The backend determines Product pricing.
Purchase-time prices are preserved.
The Order total is derived from OrderItems.
The resulting Order belongs
to the requesting Customer.
Successful creation reduces Inventory.
Any invalid item causes the whole
operation to fail.
Failure does not leave partial Inventory
or Order state.
This is a larger ticket than some earlier ones, but it still represents one coherent workflow.
Why We Do Not Split Order Creation Into Tiny Tickets
We could create separate tickets for:
validate quantity
check Product
calculate total
consume Inventory
save Order
But those pieces are strongly connected.
Individually, they provide little useful behaviour.
A better boundary is:
Domain foundation
Persistence foundation
Business workflow
HTTP exposure
That produces coherent and reviewable changes.
BACKEND-113 — Expose Order Creation API
Context
The completed Order creation workflow must be exposed through the REST API.
Scope
Define request representation.
Validate transport-level input.
Resolve Customer identity at the boundary
once Security is integrated.
Invoke CreateOrderUseCase.
Return the created Order representation.
Map expected failures to the
standard API error contract.
Acceptance Criteria
A valid request invokes
the existing Order creation UseCase.
Malformed or invalid request shapes
are rejected.
Client-provided Product prices
are not accepted as authoritative pricing.
Known business failures are represented
through the standard API error contract.
The public API is documented.
Dependencies
BACKEND-112
Security enforcement will be wired when the Authentication and Authorization epic is implemented.
Why API and UseCase Are Separate
They own different responsibilities.
The workflow should be testable without HTTP.
The API boundary handles:
HTTP
JSON
request validation
response mapping
error representation
The UseCase handles:
application workflow
business decisions
Repository coordination
transaction boundary
Our architectural direction is:
Handler
↓
UseCase
↓
Repository / Domain
Service remains reserved for external integrations.
BACKEND-114 — Expose Customer Order History and Detail
Context
Customers need to review their previous Orders.
Scope
Retrieve Orders belonging
to a specific Customer identity.
Provide bounded history retrieval.
Provide Order detail.
Preserve historical item information.
Acceptance Criteria
Only the Customer's own Orders
are visible through Customer-facing access.
History is bounded.
History ordering is deterministic,
newest first.
Historical unit prices remain unchanged
when current Product prices change.
Cancelled and PAID Orders remain
visible in history.
Order detail exposes the data required
to understand the historical Order.
No Product price lookup is used to reconstruct historical pricing.
BACKEND-115 — Implement Order Cancellation
Context
Customers may cancel eligible UNPAID Orders.
Scope
Load the Customer-owned Order.
Validate cancellation eligibility.
Transition the Order to CANCELLED.
Restore Inventory consumed by that Order.
Maintain transactional consistency.
Prevent double restoration.
Acceptance Criteria
A Customer can cancel their own UNPAID Order.
A PAID Order cannot be cancelled in v1.
An already-CANCELLED Order
cannot be cancelled again.
Successful cancellation restores
the consumed Inventory.
Order state and Inventory restoration
are atomic.
Concurrent cancellation attempts
must not restore Inventory twice.
Paid cancellation and refunds remain outside v1.
Security Work
The Product Stories require authenticated Customer and Administrator behaviour.
Security backlog:
BACKEND-116
Integrate Authenticated Identity
BACKEND-117
Enforce Customer Resource Ownership
BACKEND-118
Enforce Administrator Permissions
BACKEND-116 — Integrate Authenticated Identity
Context
An external identity capability authenticates users.
Our backend must consume the authenticated identity.
Scope
Configure Spring Security.
Resolve authenticated identity.
Provide CustomerId to Customer UseCases.
Reject protected requests
without valid authentication.
Out of Scope
User registration
Password storage
Password reset
Identity provider implementation
BACKEND-117 — Enforce Customer Resource Ownership
Context
Customers must not view or mutate another Customer's Orders.
Acceptance Criteria
A Customer can access their own Orders.
A Customer cannot retrieve
another Customer's Order.
A Customer cannot cancel
another Customer's Order.
A Customer cannot pay
another Customer's Order.
Ownership behaviour is
integration-tested.
For Customer-facing Order lookup, missing and non-owned resources may intentionally produce the same public ORDER_NOT_FOUND result.
That avoids exposing another Customer's resource existence.
BACKEND-118 — Enforce Administrator Permissions
Context
Product and Inventory administration must be restricted to Administrators.
Acceptance Criteria
Authorized Administrators can perform
supported Product operations.
Authorized Administrators can perform
supported Inventory operations.
Regular Customers cannot create,
update, or deactivate Products.
Regular Customers cannot modify Inventory.
Authorization behaviour is
integration-tested.
Payment Work
Payment crosses an external system boundary and deserves its own sequence.
The initial backlog can include:
BACKEND-119
Define Payment Integration Boundary
BACKEND-120
Implement Payment Provider Integration
BACKEND-121
Implement Order Payment Workflow
BACKEND-122
Expose Order Payment API
Why Start With an Integration Boundary?
We do not want provider-specific HTTP code inside Order business logic.
Our application should depend on the capability it needs.
Conceptually:
PayOrderUseCase
↓
PaymentService
↓
External Payment Provider
Here, PaymentService is an appropriate use of the word Service because it represents an external integration.
BACKEND-119 — Define Payment Integration Boundary
Context
Order Payment logic needs an external payment capability without depending on provider-specific HTTP details.
Scope
Define only the application-facing Payment contract required by v1.
Acceptance Criteria
Order Payment logic can request
the provider capability it needs.
Provider-specific HTTP details
do not leak into the Order Domain.
The boundary is limited to
current Payment requirements.
Successful and failed provider outcomes
can be represented.
Out of Scope
Refunds
Multiple Payment providers
Stored cards
General-purpose Payment platform
Unrequired webhook architecture
BACKEND-120 — Implement Payment Provider Integration
Context
The external Payment boundary must communicate with the configured provider.
Scope
Call the external provider.
Externalize provider configuration.
Handle expected responses.
Apply timeout behaviour.
Keep provider-specific mapping
inside the integration layer.
Acceptance Criteria
The configured provider can be called.
Successful provider responses
are translated into application results.
Provider failures are not interpreted
as successful Payment.
Timeouts are handled explicitly.
Provider URL and credentials
are not hardcoded.
Retry and idempotency behaviour will be designed carefully in the external-services module rather than guessed here.
BACKEND-121 — Implement Order Payment Workflow
Context
A Customer needs to pay an eligible Order.
Scope
Validate Customer ownership.
Validate that the Order is UNPAID.
Interact with PaymentService.
Apply confirmed successful Payment.
Do not mark the Order PAID
when Payment fails.
Prevent invalid state transitions.
Acceptance Criteria
A Customer may pay only
for their own Order.
Only an UNPAID Order is payable.
A CANCELLED Order cannot be paid.
An already-PAID Order cannot
transition to PAID again as a new action.
Confirmed successful Payment
marks the Order PAID.
Failed or unconfirmed Payment
does not mark the Order PAID.
External idempotency and the difficult pay-vs-cancel concurrency problem will be addressed during Payment integration design.
We will not hold a database row lock across a remote provider call.
BACKEND-122 — Expose Order Payment API
Scope
Expose the Payment workflow through the REST API.
Acceptance Criteria
A valid request invokes
the existing Payment workflow.
Expected Payment failures
use the API error contract.
Provider-specific internals
are not unnecessarily exposed.
The API is documented.
Production Readiness Work
Later modules introduce deliberate operational capabilities.
High-level backlog items can already exist:
BACKEND-123
Add Structured Application Logging
BACKEND-124
Add Request Correlation
BACKEND-125
Expose Application Health Checks
BACKEND-126
Add Application Metrics
BACKEND-127
Containerize the Application
BACKEND-128
Create CI Build and Test Pipeline
These tickets do not need every implementation detail now.
We will refine them when their modules begin.
Testing Is Not a Final Ticket
A poor backlog might say:
Build all features.
Then:
BACKEND-999
Add tests.
We will not work that way.
Testing is part of every relevant ticket's Definition of Done.
For example:
BACKEND-112
Implement Order Creation Workflow
already includes relevant automated testing.
The dedicated Testing module will teach test strategy in greater depth and strengthen the system further.
But automated testing begins during implementation.
Documentation Is Not a Final Cleanup Ticket Either
Likewise:
Build everything.
Document it later.
is a poor strategy.
Relevant documentation evolves with the code.
For example:
API change
→ API documentation
important architecture decision
→ ADR
larger technical proposal
→ RFC
The dedicated documentation/workflow lessons improve the practice later, but context should not be postponed until everyone forgets why a decision was made.
We Are Not Ready to Implement Every Ticket Yet
We now have a backlog.
That does not mean every ticket is immediately ready to start.
For example:
BACKEND-112
Implement Order Creation Workflow
requires design decisions around:
Domain model
Inventory concurrency
transaction boundary
Repository contracts
Those decisions belong in the technical-design modules.
This is realistic.
A backlog can exist before every implementation detail has been finalized.
Backlog Does Not Mean Ready
A future ticket may exist as:
BACKEND-121
Implement Order Payment Workflow
while still being:
Not Ready
because external integration behaviour has unresolved questions.
The backlog represents planned work.
Ready work is a subset of the backlog.
Key Dependencies
Let's summarize the dependency structure.
Foundation:
BACKEND-101 Bootstrap
↓
BACKEND-102 PostgreSQL + Flyway
Product:
BACKEND-103 Product Domain
↓
BACKEND-104 Product Persistence
Inventory:
BACKEND-106 Inventory Model + Persistence
↓
BACKEND-107 Inventory Administration
Product browsing:
Product Persistence
+
Inventory Persistence
↓
BACKEND-108 Product Browsing
Orders:
Product
+
Inventory
+
CustomerId representation
↓
BACKEND-110 Order Domain
↓
BACKEND-111 Order Persistence
↓
BACKEND-112 Order Creation UseCase
↓
BACKEND-113 Order Creation API
History and cancellation:
Order Persistence / Workflow
↓
BACKEND-114 Order History + Detail
↓
BACKEND-115 Cancellation
Security:
Authenticated Identity
↓
Customer ownership enforcement
+
Administrator authorization
Payment:
Order Domain
+
Payment Integration Boundary
↓
Payment Provider Integration
↓
PayOrderUseCase
↓
Payment API
Not Every Dependency Is a Hard Code Dependency
For example, Product browsing does not necessarily require authentication.
The Product brief says Customers browse Products.
It does not say Product browsing must be authenticated.
So we should not invent that restriction.
By contrast:
view own Orders
cancel own Order
pay own Order
administer Products
administer Inventory
clearly require identity or authorization enforcement.
Backlog dependencies should follow actual requirements rather than blanket rules.
Prioritizing the Backlog
A practical implementation sequence after design might be:
1. Bootstrap application
2. PostgreSQL and Flyway foundation
3. Product Domain
4. Product persistence
5. Product administration
6. Inventory Domain and persistence
7. Inventory administration
8. Product browsing
9. CustomerId application representation
10. Order Domain
11. Order persistence
12. Order creation UseCase
13. Order creation API
14. Order history and detail
15. Order cancellation
16. Authentication and authorization
17. Payment integration
18. Production-readiness work
19. Shipping
The exact order may change slightly after technical design.
That is normal.
Why Payment Is Not First
Payment sounds important.
But Payment requires an Order.
An Order requires Products and Inventory.
Building Payment first would either:
force temporary mocks and fake Domain objects everywhere
or:
encourage abstractions before we understand
the actual application workflow
Stable prerequisites should come first.
Why Production Concerns Are Not Ignored Until the End
The dedicated Production Readiness module comes later.
That does not mean early code should have no logs, error handling, or operational awareness.
Basic engineering hygiene exists from the beginning.
Later modules make those capabilities intentional and systematic.
The balance is:
Do not ignore production concerns, but do not front-load advanced operational complexity before the core application exists.
Vertical Slices
After the minimum foundation exists, feature work should generally move vertically.
For example, Product creation may involve:
HTTP
↓
Handler
↓
CreateProductUseCase
↓
Product Domain
↓
ProductRepository
↓
PostgreSQL
plus:
tests
migration
API documentation
After that change is complete, one real capability works end to end.
That is more valuable than completing:
all Handlers
then:
all Repositories
while no actual business capability works.
Horizontal Foundation vs Vertical Feature
Some work is naturally horizontal:
Spring Boot bootstrap
PostgreSQL configuration
Flyway configuration
security framework setup
API error foundation
Feature work is often better implemented vertically.
A healthy application uses both.
Bad extreme:
Build six months of infrastructure
before the first feature.
Other bad extreme:
Duplicate every shared concern
inside every feature.
Our backlog aims for:
minimal shared foundation
+
meaningful vertical capabilities
Tickets Should Be Reviewable
Imagine a Pull Request containing:
4,000 changed lines
database schema
security
Orders
Payments
Docker
CI
The reviewer's cognitive load is enormous.
Review quality drops even if the code is technically good.
Smaller coherent tickets tend to produce focused PRs.
For example:
BACKEND-104
Persist Products
might result in a PR containing:
migration
persistence mapping
Repository adapter
integration tests
One meaningful change.
One understandable review.
But Do Not Optimize for the Smallest Possible Diff
A tiny PR can be good.
But splitting one cohesive feature into ten dependent PRs can create:
temporary inconsistent states
review overhead
coordination overhead
The goal is:
The smallest change that still makes engineering sense.
Not:
The smallest possible diff.
Ticket Scope and Pull Request Scope
A practical guideline is:
one bounded ticket
≈
one focused Pull Request
It is not an absolute rule.
Some larger tickets may require several PRs.
Sometimes several tiny related tickets may share a PR.
But one-ticket-to-one-primary-PR is useful for course traceability.
Tickets Should Explain Why
Weak:
Add unitPriceCents to OrderItem.
Better:
Preserve purchase-time pricing
for OrderItems.
Context:
Historical Orders must not change
when current Product prices change.
Now an engineer understands the intent.
If the implementation changes later, the underlying requirement remains clear.
Avoid Solution-First Tickets Too Early
Suppose we add:
Use pessimistic locking for Inventory.
to the backlog before concurrency design is complete.
We have silently converted an implementation guess into a plan.
Better:
Ensure concurrent Order creation
cannot oversell Inventory.
Then technical design determines the mechanism.
In our eventual design, that problem can be solved using an atomic conditional Inventory update.
The backlog should describe the problem before design chooses the solution.
Spikes and Investigations
Sometimes an implementation ticket cannot be made ready because important technical information is unknown.
A team may create a short investigation or spike.
Example:
Investigate Payment provider
idempotency support.
Useful output might include:
provider capabilities
limitations
recommended integration approach
But spikes should exist only for genuine uncertainty.
We do not create fake research tickets simply to make the workflow appear more realistic.
Avoid Bureaucratic Theatre
A poor simulation might create:
Investigate whether PostgreSQL supports transactions.
We already know that.
There is no real uncertainty.
Authentic engineering process means investigating genuine unknowns, not pretending established facts are mysterious.
Bugs Are Backlog Items Too
Once the application exists, the backlog evolves.
A production issue may create:
BUG-201
Prevent duplicate Order creation
under an identified retry scenario.
An incident may create corrective work:
BACKEND-240
Add monitoring for a specific
Payment reconciliation failure.
Backlogs evolve with the system.
They are never permanently finished.
Product Change Creates Backlog Change
Later, when a production change request arrives, we will not restart planning from zero.
We will:
understand the new requirement
↓
assess impact
↓
update design where necessary
↓
add, modify, or remove backlog items
That is how long-lived systems evolve.
Initial Backlog by Epic
We can now organize the work into larger groups.
Epic 1 — Application Foundation
BACKEND-101
Bootstrap Order Management Backend
BACKEND-102
Configure PostgreSQL and Flyway
Epic 2 — Product Management
BACKEND-103
Model the Product Domain
BACKEND-104
Persist Products
BACKEND-105
Implement Product Administration
Epic 3 — Inventory Management
BACKEND-106
Model and Persist Inventory
BACKEND-107
Implement Inventory Administration
Epic 4 — Product Discovery
BACKEND-108
Expose Available Product Browsing
Epic 5 — Customer Identity Context
BACKEND-109
Represent Customer Identity
for Application Workflows
This is not a Customer management subsystem.
Epic 6 — Ordering
BACKEND-110
Model Order and OrderItem Domain
BACKEND-111
Persist Orders and OrderItems
BACKEND-112
Implement Order Creation Workflow
BACKEND-113
Expose Order Creation API
BACKEND-114
Expose Customer Order History and Detail
BACKEND-115
Implement Order Cancellation
Epic 7 — Security
BACKEND-116
Integrate Authenticated Identity
BACKEND-117
Enforce Customer Resource Ownership
BACKEND-118
Enforce Administrator Permissions
Epic 8 — Payment
BACKEND-119
Define Payment Integration Boundary
BACKEND-120
Implement Payment Provider Integration
BACKEND-121
Implement Order Payment Workflow
BACKEND-122
Expose Order Payment API
Epic 9 — Production Readiness
BACKEND-123
Add Structured Application Logging
BACKEND-124
Add Request Correlation
BACKEND-125
Expose Application Health Checks
BACKEND-126
Add Application Metrics
Epic 10 — Delivery
BACKEND-127
Containerize the Application
BACKEND-128
Create CI Build and Test Pipeline
This is our initial backlog.
It will evolve as our understanding improves.
What should not evolve casually is the Product scope.
What Is Missing From the Backlog?
Notice that several course topics are not represented as standalone tickets:
Dependency Injection
Spring Beans
HTTP status codes
JPA
JUnit
Mockito
@Transactional
Why?
Because these are learning topics, not Product work.
We will learn them while implementing real engineering tickets.
For example:
Dependency Injection
appears naturally while wiring Handlers, UseCases, Repositories, and external Services.
JPA
appears while implementing persistence.
JUnit
appears while satisfying the Definition of Done.
@Transactional
appears when Order workflows create a real atomicity requirement.
This is an important course-design principle.
Curriculum and Backlog Are Different
The curriculum asks:
What engineering concept
does the learner need to understand?
The backlog asks:
What does the engineering team
need to deliver?
They intersect.
They are not the same.
Example:
Course lesson:
Database Transactions
Engineering ticket:
BACKEND-112
Implement Order Creation Workflow
We learn transactions because the real workflow creates an atomicity problem.
We do not invent work simply to demonstrate a technology.
Backlog Refinement
The backlog is not created once and then frozen forever.
Before implementation, a team may refine a ticket by:
clarifying scope
adding Acceptance Criteria
identifying dependencies
splitting oversized work
removing unnecessary work
raising technical questions
For example, technical design may show that:
BACKEND-105
Implement Product Administration
should become separate tickets for:
Product creation
Product update
Product deactivation
That would be a reasonable refinement.
Do Not Split Before Understanding the Design
If we split too early into implementation details such as:
BACKEND-X
Lock Inventory row
BACKEND-Y
Use transaction propagation X
BACKEND-Z
Add Repository method Y
we have already assumed the solution.
A healthier sequence is:
Understand requirement
↓
Design solution
↓
Refine implementation work
Design and backlog refinement inform each other.
Ready Ticket Example
A reasonably ready ticket might look like this:
# BACKEND-108 — Expose Available Product Browsing
## Context
Customers need to browse Products that can currently be ordered.
## Scope
Expose bounded retrieval of Products that satisfy
the v1 orderability rules.
## Acceptance Criteria
- Product is active.
- Product has available Inventory greater than zero.
- Product price is included.
- Retrieval is bounded.
- Results are deterministic.
## Out of Scope
- Product search
- Recommendations
- Categories
- Full-text filtering
## Dependencies
- BACKEND-104
- BACKEND-106
An engineer can understand the expected outcome without inventing hidden features.
Not-Ready Ticket Example
# BACKEND-121 — Implement Order Payment Workflow
## Open Questions
- Exact provider idempotency behaviour is not yet confirmed.
- Timeout and retry semantics require integration design.
- Provider-success / local-state-failure behaviour needs a design.
The ticket can exist in the backlog without being ready to start.
That is better than hiding uncertainty.
Backlog Priority Can Change
Suppose we discover that the Payment provider requires a long external onboarding process.
The team may move Payment investigation earlier even though Payment implementation remains later.
Priority depends on:
business value
dependencies
risk
uncertainty
Ticket number does not determine execution order.
Risk-Driven Planning
Sometimes a technically risky question deserves early design attention.
For example:
Can concurrent Order creation
oversell Inventory?
That question affects fundamental correctness.
It should be understood before we spend time polishing low-risk details.
Good planning balances:
value
+
dependencies
+
risk
Avoid Infrastructure “Just in Case”
Our backlog should not contain:
Add Kafka for future events.
Add Redis in case reads become slow.
Create Elasticsearch cluster.
Add Kubernetes manifests
before deployment requires them.
None of those satisfy a current requirement.
A future possibility is not a current need.
Every Ticket Creates Maintenance Cost
A useful mental model:
More functionality
↓
More code
↓
More tests
↓
More operational surface
↓
More future maintenance
Every backlog item should earn its existence.
Ask:
Which Product requirement or engineering need does this ticket satisfy?
If there is no clear answer, the ticket may not belong in the backlog.
Traceability
Our work is now traceable.
For Product browsing:
Product Requirement
Customers can browse available Products.
↓
STORY-1
Browse Available Products
↓
BACKEND-103
Product Domain
↓
BACKEND-104
Product Persistence
↓
BACKEND-106
Inventory Persistence
↓
BACKEND-108
Product Browsing
↓
Pull Requests
↓
Tests
For Order creation:
Product Requirement
Customers can place Orders.
↓
STORY-2
Create an Order
↓
BACKEND-110
Order Domain
↓
BACKEND-111
Order Persistence
↓
BACKEND-112
CreateOrderUseCase
↓
BACKEND-113
Order Creation API
Traceability helps engineers answer:
Why does this code exist?
Backlog Is Not Architecture
We now have tickets such as:
Persist Orders
But the backlog still does not answer:
What exact tables?
Which Java classes?
Which package structure?
What Repository contracts?
Where is the transaction boundary?
How should modules depend on each other?
How is Inventory concurrency handled?
Those are technical-design questions.
That is why the next module exists.
Our First Planned Milestones
A reasonable first milestone could be:
Application starts
+
PostgreSQL works
+
Products can be managed
+
Inventory can be managed
+
Available Products can be browsed
Then:
Order creation works
Then:
Order history
+
Order cancellation
Then:
Authentication / Authorization
+
Payment
Then:
Production readiness
+
Shipping
The exact release boundaries may evolve.
But incremental milestones make progress visible.
What “Milestone” Means Here
A milestone is not a new Product feature.
It represents a meaningful state of the project.
For example:
Milestone 1
Catalog and Inventory foundation works.
This is useful for learning as well.
The learner sees the application evolve instead of waiting until the end of the course for a runnable system.
Backlog and Definition of Done Work Together
A ticket tells us:
What change are we making?
Acceptance Criteria tell us:
What behaviour must it satisfy?
Definition of Done tells us:
What quality conditions
must be satisfied?
Together:
Ticket
+
Acceptance Criteria
+
Definition of Done
↓
Meaningful engineering work
A Ticket Is Not Done Because Every Subtask Is Checked
Suppose a ticket contains:
[x] Handler
[x] UseCase
[x] Repository
[x] Migration
but inactive Products can still be ordered.
The feature is not Done.
Subtasks describe implementation activity.
Acceptance Criteria describe behaviour.
When they conflict:
Behaviour wins.
Avoid Ticket-Driven Design
Suppose a backlog item says:
Create Repository.
After design, we realize that exact abstraction is unnecessary.
We should refine the ticket.
We should not preserve a poor implementation decision merely because someone typed it into the backlog earlier.
The backlog is a planning tool.
It is not an architectural law.
The Backlog Will Change
Backlogs evolve because:
requirements change
technical discovery happens
dependencies shift
risks appear
tickets are split
tickets become unnecessary
Changes should be deliberate.
We should not casually rewrite Product scope or introduce new features without a reason.
Module 1 Deliverables
At the end of Module 1, our project conceptually has:
Product Brief
Requirement Analysis
Functional Requirements
Non-Functional Concerns
User Stories
Acceptance Criteria
Engineering Context
Definition of Done
Initial Engineering Backlog
That is substantial engineering preparation.
But none of it exists simply for paperwork.
Each artifact answers a different question.
