Joining the Backend Team
Understanding the Existing Engineering Context
So far, we have:
- understood the Product requirement
- separated functional and non-functional requirements
- created User Stories
- defined initial Acceptance Criteria
At this point, a beginner-level tutorial might start coding.
A real engineering team usually has another important step first:
What technical and organizational environment will this system live in?
Software is never built in a vacuum.
Even when creating a completely new backend application, there are usually existing:
- platform constraints
- technology conventions
- deployment environments
- security expectations
- external dependencies
- delivery practices
- observability infrastructure
- team conventions
If an engineer ignores that context, they may design something that is technically reasonable in isolation but does not fit the organization or production environment.
In this lesson, we will establish the engineering context for our Order Management Backend.
We are not adding new Product features.
We are defining the environment in which the already-agreed Product scope will be implemented.
You Rarely Start From Nothing
Imagine that you have just joined a company.
A Product Manager tells you:
We need a new Order Management Backend.
That usually does not mean:
Choose any programming language.
Choose any database.
Invent your own API conventions.
Deploy anywhere you want.
Create your own authentication mechanism.
Build your own logging infrastructure.
Use any build tool you prefer.
The company may already have established decisions.
For example:
Backend language:
Java
Framework:
Spring Boot
Database platform:
PostgreSQL
Source control:
Git
CI:
Existing company pipeline
Deployment:
Containerized applications
Authentication:
Existing identity capability
API style:
REST over HTTP using JSON
Observability:
Centralized logs and metrics
One of a new engineer's first responsibilities is to understand this environment.
Product Context vs Engineering Context
Our Product requirement says:
Customers should be able to place Orders.
That is Product context.
Engineering context might tell us:
The application must be implemented in Java.
The company uses PostgreSQL for transactional systems.
Applications are packaged as containers.
Authentication is provided by an existing identity capability.
Backend APIs use REST over HTTP with JSON.
The Product behaviour has not changed.
But the engineering environment constrains some implementation decisions.
This distinction is important:
Product Context
→ What behaviour do we need?
Engineering Context
→ What environment and constraints
must the solution fit into?
Constraints Are Not Always Bad
The word constraint can sound negative.
But many constraints are valuable.
Suppose an organization says:
Backend applications use Java.
That removes some technical freedom.
But it may also provide:
shared expertise
shared libraries
consistent tooling
simpler support
common deployment practices
easier maintenance
Similarly:
Use PostgreSQL.
limits database choice.
But if the organization already operates PostgreSQL successfully, introducing a completely different database without a strong reason may create unnecessary operational cost.
Good engineering does not mean maximizing the number of available choices.
It often means:
Given the existing constraints, choose an appropriate solution.
Our Project Engineering Context
For this course, we will establish a stable engineering context for the Order Management Backend.
Unless a later requirement explicitly changes something, we will work within these boundaries:
Language:
Java
Framework:
Spring Boot
Build Tool:
Gradle
API Style:
REST over HTTP using JSON
Primary Database:
PostgreSQL
Database Migrations:
Flyway
Source Control:
Git
Testing:
JUnit-based automated testing
External Dependency:
Payment provider HTTP API
Authentication:
An existing identity capability provides
authenticated users
Deployment:
Containerized backend application
Observability:
Existing centralized logging and
metrics infrastructure
Let's examine what each decision means.
Java Is Already a Team Standard
The implementation language is:
Java
We are not going to begin by benchmarking Java against every other programming language.
There are two reasons.
First, this course assumes you are moving from Java and Object-Oriented Programming into backend development.
Second, in our simulated company context, Java is already an established backend technology.
This reflects a useful real-world lesson:
A new project does not automatically require a new programming-language decision.
When an organization already has a successful standard stack, using that stack is often the sensible default.
Spring Boot Is the Application Framework
Our team uses:
Spring Boot
for Java backend applications.
But Spring Boot is not the goal of this course.
The goal is:
Build a production-style
Order Management Backend.
Spring Boot is:
One of the tools we use to build it.
This distinction will remain important throughout the course.
We will learn Spring Boot capabilities because the project creates a need for them.
Why We Are Not Comparing Frameworks
We could spend time comparing:
Spring Boot
Quarkus
Micronaut
Jakarta EE
But our engineering context already establishes Spring Boot.
A framework comparison would not move this project forward.
Real engineering environments often eliminate many decisions before a feature even reaches a developer.
That can be beneficial.
It reduces unnecessary decision-making and helps teams maintain consistency.
Gradle Is the Existing Build Tool
Our project will use:
Gradle
Later, Gradle will handle tasks such as:
compile
test
package
dependency management
build automation
This is not a Gradle deep-dive course.
We will learn the parts required to work effectively on the backend application.
REST and JSON Are the API Convention
The company's backend applications conventionally expose HTTP-based APIs using:
REST-style resources
JSON
So a Product requirement such as:
Customer can create an Order.
will eventually be exposed through an HTTP API.
Something conceptually similar to:
POST /orders
may emerge later.
But we are not finalizing endpoint design in this lesson.
At this stage, the relevant engineering convention is simply:
HTTP + JSON + REST-style APIs
The actual API contract comes later.
PostgreSQL Is the Transactional Database
The primary persistent data store for the Order Management Backend is:
PostgreSQL
This is part of the existing engineering platform.
Our domain includes related concepts such as:
Products
Inventory
Orders
OrderItems
and later order-related payment information.
Order workflows also require multiple related state changes to remain consistent.
PostgreSQL gives us a strong transactional persistence foundation.
But we are not designing the database schema yet.
Database Choice and Database Design Are Different
We already know:
We use PostgreSQL.
We do not yet know:
Which tables should exist?
What relationships should they have?
Which constraints belong in the database?
Which indexes are necessary?
Where should transaction boundaries live?
Those are technical design decisions for later lessons.
Choosing a technology does not complete the system design.
Database Changes Must Be Versioned
Our production database schema will not be manually edited whenever the application changes.
Schema evolution must be:
versioned
reviewable
repeatable
We will use:
Flyway
for database migrations.
Later, our repository may contain files such as:
V1__create_products.sql
V2__create_inventory.sql
V3__create_orders.sql
The exact migration sequence will emerge from our implementation plan.
The principle is more important:
Database changes should be controlled and reproducible just like application code.
Authentication Is Not Our Identity Platform
The Product requirements distinguish between:
Customer
Administrator
because they have different permissions.
But our Order Management Backend will not implement a new identity provider.
The engineering context says:
An existing identity capability
authenticates users.
Our backend is responsible for:
receiving authenticated identity
determining allowed operations
enforcing resource ownership
enforcing permissions
Conceptually:
Existing Identity Capability
↓
Authenticated Identity
↓
Order Management Backend
↓
Authorization Decisions
Later, Spring Security will help us implement this boundary.
Why This Boundary Matters
Imagine that we started implementing:
password storage
password reset
email verification
account recovery
identity verification
inside the Order Management Backend.
The scope would expand dramatically.
But our responsibility is:
Order Management Backend
not:
Identity Platform
Maintaining clear system boundaries is a core backend engineering skill.
Customer Identity Is External Context
The Order Management Backend still needs to know:
Which Customer owns this Order?
But it does not need a full local Customer management subsystem to answer that.
Instead:
authentication layer
↓
stable authenticated user identity
↓
CustomerId used by Order workflows
Later, that CustomerId becomes part of application operations such as:
Create Order
View Own Orders
Cancel Own Order
Pay Own Order
The identity system owns authentication.
The ordering system owns the relationship between authenticated identity and Order data.
An External Payment Provider Already Exists
Our application does not perform actual card or payment-network processing itself.
The Product requirement already tells us that payment happens through an external provider.
Conceptually:
Customer
↓
Order Management Backend
↓
Payment Provider
Our responsibility includes:
initiate the appropriate payment interaction
handle provider responses
apply confirmed payment outcomes
to Order state
handle failures safely
prevent unintended duplicate processing
The provider owns the actual external payment execution.
Payment State vs Payment Processing
This distinction matters.
Our system needs to know when an Order has successfully become:
PAID
But it does not own the internal mechanics of:
card networks
bank authorization
provider settlement systems
Conceptually:
Payment Provider
→ owns external payment execution
Order Management Backend
→ owns the Order lifecycle consequence
relevant to payment
We will refine this boundary when we implement external Payment integration.
External Systems Are Unreliable by Definition
The Payment provider may be highly reliable.
But communication across a network can still fail.
We need to consider possibilities such as:
timeout
connection failure
provider error
unexpected response
duplicate request
slow response
We are only identifying these risks now.
We are not implementing retry or idempotency policies yet.
Those belong in the external integration module.
Source Control Is Git
The team manages source code using:
Git
Git is not simply a backup mechanism.
It supports our development workflow:
ticket
↓
change
↓
commit
↓
Pull Request
↓
review
↓
merge
Ideally, the repository history should reflect incremental development rather than one enormous final change.
We Work Through Pull Requests
Changes are reviewed through Pull Requests before they are merged.
A reviewer may inspect:
implementation
tests
design impact
risk
documentation
Later in the course, we will simulate:
PR descriptions
code review
review feedback
follow-up changes
This matters now because it influences how we implement work.
We want:
small
coherent
reviewable
changes.
The Application Will Be Containerized
The deployment environment runs the backend as a containerized application.
Eventually:
Java Application
↓
Container Image
↓
Runtime Environment
We are not writing the Dockerfile yet.
But knowing the deployment model helps us avoid incorrect assumptions.
For example, the application should not treat its local filesystem as permanent business storage.
Application Instances Should Be Replaceable
In a containerized environment, an application instance may be:
started
stopped
replaced
restarted
Important business state should therefore not depend permanently on:
process memory
local application disk
Persistent business state belongs in appropriate durable storage such as:
PostgreSQL
This also gives us a better foundation if the application later runs with more than one instance.
Multiple Environments Exist
A real application does not run only on:
localhost
Conceptually, we may have:
Local Development
↓
Test / CI
↓
Non-Production
↓
Production
Those environments do not necessarily have identical infrastructure.
But the application must be configurable for different environments.
Later, we will use:
Spring configuration
environment variables
to accomplish that.
Configuration Must Not Be Hardcoded
Bad:
String databasePassword =
"super-secret-password";
Also bad:
String paymentUrl =
"https://production-payment.example";
hardcoded throughout the application.
Configuration such as:
database connection
Payment provider URL
credentials
environment-specific behaviour
must come from external application configuration.
Spring Boot will help us implement this later.
Secrets Are Not Application Code
Credentials, tokens, passwords, and similar secrets must not be committed into the source repository.
The principle is:
Application Code
≠
Secrets
We are not building a secret-management platform in this course.
But we will design the application so that sensitive values can be supplied through runtime configuration.
Centralized Logging Already Exists
The organization already has centralized logging infrastructure.
Our application's responsibility is:
produce useful logs
not:
build a centralized logging platform
Later, we will implement:
structured logging
request/correlation identifiers
useful error context
The distinction is:
Observability Infrastructure
versus:
Application Instrumentation
They are related, but they are not the same responsibility.
Metrics Infrastructure Already Exists
Likewise, the organization already has a way to collect metrics.
Our backend will eventually expose useful signals about:
request behaviour
application health
important failures
We will decide which metrics matter later.
We will not invent arbitrary dashboards or measure everything simply because a metrics platform exists.
Health Checks Are Expected
The deployment environment needs to determine whether the application is healthy.
Later, Spring Boot Actuator will help expose health information.
This is not a customer-facing Product feature.
But it is still a real production engineering requirement.
Product Feature?
No.
Production Requirement?
Yes.
This is exactly why engineering context matters.
CI Is Part of the Delivery Process
Before code is merged, an automated pipeline should at minimum be able to:
build
test
the application.
Later, we will implement CI.
For now, remember:
“Works on my machine” is not a completion criterion.
A change must also succeed in the team's automated environment.
We Are Building One Application
The first version of the Order Management Backend is:
one deployable backend application
Conceptually, it contains logical capabilities for:
Products
Inventory
Orders
Payment integration
Authenticated Customer identity enters through the security boundary rather than through a local Customer subsystem.
We are not creating separate microservices for these capabilities.
One Application Does Not Mean One Giant Package
A modular monolith does not mean:
all code in one enormous package
We will maintain clear logical boundaries.
Conceptually:
product
inventory
order
payment
Authentication/security concerns will integrate with these workflows without becoming a local Customer module.
The exact package structure comes later.
For now, the architectural boundary is:
one deployable backend application
Why Not Microservices?
A commerce domain may tempt us to create:
Product Service
Inventory Service
Order Service
Payment Service
Customer Service
But our current requirements do not justify that complexity.
Splitting the system into multiple services would introduce new problems:
network communication
distributed failures
deployment coordination
distributed transactions
service discovery
observability complexity
data ownership complexity
We do not need those problems yet.
Our goal is:
Build a production-quality backend foundation.
Distributed architecture is a separate engineering problem.
Technology Does Not Define Domain Boundaries
Using one application does not mean all concepts become the same thing.
Logical boundaries still matter.
For example:
Order domain logic
should not need to understand:
Payment provider HTTP details
Likewise:
Product domain logic
should not depend directly on:
controller JSON representation
We will make these boundaries concrete in later architecture and domain-modeling lessons.
Understand Existing Conventions Before Creating New Ones
Suppose an organization already uses an API error format like:
{
"code": "RESOURCE_NOT_FOUND",
"message": "Resource was not found"
}
A new engineer should not casually introduce:
{
"oops": true,
"reason": "not there"
}
The second response is technically valid JSON.
But it breaks platform consistency.
In a real team, engineers should inspect existing conventions before creating new ones.
Our course project is greenfield, but we will still establish and follow consistent conventions as it grows.
Existing Context Checklist
When joining a backend project, a useful discovery checklist includes:
Language and framework?
Build tool?
Repository conventions?
API conventions?
Authentication mechanism?
Authorization model?
Primary database?
Migration strategy?
Deployment environment?
Configuration management?
Secrets management?
Logging platform?
Metrics platform?
CI expectations?
Testing conventions?
External dependencies?
Ownership boundaries?
Not every question matters equally in every project.
But this checklist helps prevent context-free design.
Ownership Is Important
We should understand what our system owns and what external systems own.
Order Management Backend Owns
Product state required by the ordering domain
Inventory state
Orders
OrderItems
Order lifecycle
Order-related application state
Existing Identity Capability Owns
User authentication
Identity lifecycle
Payment Provider Owns
Actual external payment processing
We are not yet defining every persistence detail.
But the responsibility boundaries are clear.
Dependency Direction
At a system-context level:
Order Management Backend
↓
PostgreSQL
Order Management Backend
↓
Payment Provider
Authenticated Identity
↓
Order Management Backend
These arrows communicate dependencies and system relationships.
A more detailed architecture diagram will come later.
For now, the important lesson is that our application does not exist in isolation.
Team Context Matters Too
Engineering context is not only about technology.
Team practices matter.
Our simulated team works with principles such as:
Requirements are reviewed before implementation.
Significant design decisions are documented.
Work is broken into tickets.
Changes should be reviewable.
Automated tests are expected.
Pull Requests explain what changed.
Production readiness is part of delivery.
Incidents result in learning and corrective action.
This is one of the defining aspects of the course.
We will not only look at the final code.
We will follow the decision-making process that leads to it.
Documentation Should Match the Size of the Decision
We do not want documentation-heavy bureaucracy.
For a small change:
Ticket + Pull Request
may be enough.
For an important architecture decision:
ADR
may be appropriate.
For a larger cross-cutting design:
RFC
may be justified.
After a significant incident:
Incident Review
may be appropriate.
Different decisions deserve different levels of documentation.
Existing Context Can Override Personal Preference
Suppose you personally prefer Maven.
But every backend application on the team uses Gradle.
Using Gradle for the new application may still be the better engineering decision because of:
shared build tooling
shared CI
team familiarity
consistent maintenance
Engineering is not always about choosing your favourite technology.
Challenge Constraints When Necessary
Existing conventions are not automatically unquestionable.
Suppose a platform constraint genuinely prevents us from satisfying an important requirement.
Then the engineer should challenge it with evidence.
A useful discussion might look like:
Current approach cannot satisfy requirement X because Y.
Option A keeps the existing platform,
but has limitation Z.
Option B introduces a new technology
with these operational costs.
Recommendation:
...
But:
I like technology X better.
is usually not enough.
Greenfield Does Not Mean Constraint-Free
Our Order Management Backend is a new application.
But:
new application
does not mean:
no constraints
We are building a new service inside an existing company ecosystem.
That is a very common real-world scenario.
Questions We Can Now Answer
We can now answer several engineering questions.
Which language?
Java
Which framework?
Spring Boot
Which primary database?
PostgreSQL
Which migration mechanism?
Flyway
Which API communication style?
REST over HTTP using JSON
Which build tool?
Gradle
What is the deployment unit?
Containerized backend application
Does the Order backend implement full identity management?
No.
Does it implement actual external payment processing?
No.
It integrates with an external Payment provider.
Questions That Are Still Open
Even with the engineering context established, many design questions remain unanswered.
For example:
What exactly are our Domain Entities?
What should the package architecture look like?
Which API resources should exist?
What is the Order lifecycle?
What database schema should we use?
Where should transaction boundaries live?
How should errors be represented?
How should Payment retries work?
Which metrics are meaningful?
These belong to later modules.
We should not prematurely answer them here.
Context Prevents Scope Creep
Without clear boundaries, a Payment requirement could expand into:
payment gateway
fraud detection
card storage
refund engine
settlement system
But our engineering context says:
External Payment provider handles
actual payment processing.
That keeps our scope narrow.
Likewise:
Existing identity capability
authenticates users.
prevents us from accidentally building an account platform.
Boundaries protect scope.
Context Also Prevents Overengineering
Suppose we already know that the initial system is:
one deployable application
Introducing a message broker merely to replace a straightforward internal call may add complexity without solving a real problem.
Bad reasoning:
Kafka is scalable,
therefore we should use Kafka.
Better:
Do our current requirements require
asynchronous cross-system event delivery?
No.
Then do not add Kafka.
Technology should be justified by a requirement or constraint.
Our Initial System Context Diagram
At this stage, the system looks roughly like this:
┌────────────────────────┐
│ Existing Identity │
│ Capability │
└────────────┬───────────┘
│
│ authenticated identity
▼
┌──────────────┐ ┌────────────────────────────┐
│ Client │─────▶│ Order Management Backend │
└──────────────┘ HTTP │ │
│ Java + Spring Boot │
└──────────┬─────────┬───────┘
│ │
persist│ │ payment interaction
▼ ▼
┌────────────┐ ┌──────────────────┐
│ PostgreSQL │ │ Payment Provider │
└────────────┘ └──────────────────┘
This is not a detailed architecture diagram.
It is a system context diagram.
Its purpose is to answer:
What is inside our system?
What is outside our system?
What does our application depend on?
System Context Diagram vs Architecture Diagram
A system context diagram stays high-level.
It shows:
our system
external actors
external dependencies
A later architecture diagram may show concepts such as:
Handlers
UseCases
Domain
Repositories
PostgreSQL adapters
external integration Services
Do not mix those levels unnecessarily.
Context Before Design
Our progression now looks like:
Product Requirement
↓
User Behaviour
↓
Acceptance Criteria
↓
Engineering Context
↓
Technical Design
The sequence matters.
Technical design must satisfy both:
requirements
and:
engineering context
Example: Context Changes Design
Imagine the same Product requirement:
Customer can place an Order.
Context A:
Single Java application
PostgreSQL
Moderate traffic
Context B:
Existing distributed commerce platform
Multiple regions
Independent Inventory service
External Pricing service
The Product capability is identical.
The technical solution may be completely different.
This is why copying an architecture from a blog post without understanding its context is dangerous.
Ask: “What Already Exists?”
A powerful question for a new engineer is:
What already exists that I should integrate with instead of rebuilding?
For our project:
Identity capability exists.
Payment provider exists.
Logging infrastructure exists.
Metrics infrastructure exists.
Deployment platform exists.
We integrate with them.
We do not rebuild them.
Ask: “What Does Our Application Own?”
Another powerful question is:
What state and behaviour is this application responsible for?
For us, the answer is primarily:
the ordering domain
including:
Products
Inventory
Orders
OrderItems
Order lifecycle
If ownership is unclear, systems begin to overlap and responsibilities become difficult to maintain.
Ask: “What Is a Hard Constraint?”
Not every convention has the same weight.
For this project:
Java
Spring Boot
PostgreSQL
Gradle
containerized deployment
are established engineering-context decisions.
But things such as:
exact package names
exact Handler structure
exact Repository implementation
exact persistence mapping
are still technical design choices.
Knowing the difference prevents premature design.
Assumption Register
In a real project, not every piece of context may be completely confirmed.
An engineer can record assumptions.
For example:
ASSUMPTION-1
The external identity capability
provides a stable user identifier.
ASSUMPTION-2
The Payment provider exposes
an HTTP API.
For this course, we treat those as given engineering context.
In a real project, we might need to verify them.
Dependency Risk
An external dependency is both a capability and a risk.
Our major external dependency is:
Payment Provider
Potential risks include:
availability
latency
API changes
timeouts
duplicate processing
PostgreSQL is also a runtime dependency.
The identity capability is another dependency.
System design must eventually account for dependency failures.
We Do Not Need to Solve Dependency Failure Yet
We are not writing retry policies now.
We are simply identifying:
This operation crosses a system boundary.
That knowledge will guide later design.
Existing Context Influences Testing
The engineering environment also affects our testing strategy.
For example:
Java
→ JUnit ecosystem
PostgreSQL
→ real database integration testing matters
HTTP Payment provider
→ external integration boundary
needs controlled testing
REST API
→ HTTP behaviour needs testing
Later, PostgreSQL integration tests will use an isolated real PostgreSQL environment rather than depending on a manually running developer database.
Existing Context Influences Deployment
Containerized deployment makes application lifecycle behaviour important.
For example:
Can the application start with valid configuration?
Can its health be checked?
Can it shut down safely?
Can database migrations be applied safely?
These concerns sit outside individual Product features but are still part of backend engineering responsibility.
Existing Context Influences Observability
Centralized logging does not mean:
System.out.println(...);
randomly throughout production code.
Application logs should be useful and machine-processable.
Likewise, having metrics infrastructure does not mean:
measure everything
We should expose signals that help engineers understand application health and important failures.
We will design those later.
Engineering Context Document
A lightweight repository document could capture our context:
# Engineering Context
## Application
Order Management Backend
## Language
Java
## Framework
Spring Boot
## Build Tool
Gradle
## API
REST / JSON over HTTP
## Persistence
PostgreSQL
## Database Migrations
Flyway
## Authentication
Provided by an existing identity capability.
## External Integrations
Payment provider over HTTP.
## Deployment
Containerized backend application.
## Delivery
Git, Pull Requests, automated CI.
## Observability
The application integrates with existing centralized
logging and metrics infrastructure.
This kind of document quickly communicates important project context.
Do Not Turn Context Into an RFC
We are still not writing a technical proposal.
Engineering context mostly records:
existing facts
existing constraints
existing boundaries
An RFC will later answer something different:
Given these requirements and this engineering context, how should we design the system?
That distinction matters.
What an Engineer Should Do on Day One
Imagine joining a real team.
Before immediately picking up a feature ticket, useful activities might include:
Read the Product context.
Run the existing application locally.
Understand the repository structure.
Read recent relevant Pull Requests.
Find architecture documentation.
Understand the deployment flow.
Understand testing conventions.
Identify external dependencies.
Understand ownership boundaries.
Ask questions where documentation is unclear.
This may not produce code immediately.
It is still valuable engineering work.
Codebase Is Also Documentation
When an existing system already exists, the code itself can provide context.
For example:
How are API errors represented?
Existing Handlers or exception mapping may answer that.
How are migrations named?
The migration directory may answer that.
How are tests structured?
Existing tests may answer that.
A good engineer inspects existing patterns before inventing new ones.
Existing Code Is Not Automatically Correct
Finding an existing pattern does not automatically mean it is the current recommended approach.
You may encounter:
duplicated logic
poor error handling
missing tests
outdated conventions
temporary workarounds
So the right question is not:
Does this pattern exist?
but:
Is this an intentional current convention?
Is it legacy technical debt?
Was it a temporary workaround?
What is the recommended pattern now?
Context discovery requires judgement.
Our Course Starts Greenfield but Simulates Team Context
Our learning repository starts from scratch.
We therefore cannot inspect an existing application on day one.
That is why we establish the team and platform context explicitly.
But as the course progresses, something important happens:
The code we write becomes the existing system.
By the time we reach the later change-request module, we are no longer working on a greenfield application.
We will have:
existing database schema
existing API contracts
existing tests
existing historical Orders
existing production behaviour
Changing the system safely will require understanding what already exists.
That evolution is intentional.
The Context Becomes More Important Over Time
At first, the application will be small.
The engineering context may feel simple.
Later, when a production change arrives:
existing database
existing clients
existing behaviour
existing tests
existing data
will constrain what we can safely change.
At that point:
just change the code
will no longer be a safe engineering strategy.
The course is designed to let you experience that transition.
Engineering Principle
The core principle from this lesson is:
A good technical solution is not merely correct in isolation. It must fit the system, team, constraints, dependencies, and operational environment around it.
Another useful way to express this is:
Requirements
+
Engineering Context
=
Inputs to Technical Design
Requirements without context produce incomplete design.
Technology context without Product requirements loses sight of the problem.
We need both.
Our Context Is Now Established
The starting engineering context for the Order Management Backend is now clear:
Java
Spring Boot
Gradle
REST / JSON over HTTP
PostgreSQL
Flyway
Git
Pull Requests
Automated CI
Containerized deployment
Existing identity capability
External Payment provider
Centralized logging and metrics
Our initial architecture boundary is:
One deployable backend application
And intentionally:
No microservices
No Kafka
No Redis
No distributed architecture
because the current requirements do not justify them.
What We Have Not Added
Engineering context does not expand Product scope.
We still have not added:
shipping
discounts
refunds
multiple warehouses
notifications
recommendations
event streaming
Engineering context defines the environment in which we implement the existing scope.
It does not invent new Product capabilities.
Where We Are in Module 1
Our journey is now:
Product Requirement
↓
Requirement Analysis
↓
Functional vs Non-Functional Requirements
↓
User Stories and Acceptance Criteria
↓
Engineering Context
↓
Definition of Done
↓
Initial Engineering Backlog
After the next two lessons, we will be ready to move from Product understanding into implementation planning.
Summary
In this lesson, we learned that:
- Software is not built in isolation.
- Product requirements and engineering context are different inputs.
- Existing technology and platform constraints influence technical design.
- Constraints are not automatically bad; consistency and operational simplicity have value.
- A new project does not require every technology decision to be reopened.
- Our Order Management Backend uses Java, Spring Boot, Gradle, PostgreSQL, Flyway, and REST/JSON.
- Database changes will be versioned through Flyway migrations.
- Authentication comes from an existing identity capability.
- We are not building an identity platform or local Customer account-management subsystem.
- The backend uses authenticated identity as Customer context for Order workflows.
- Actual external payment execution belongs to the Payment provider.
- The backend owns Order lifecycle behaviour related to confirmed Payment outcomes.
- External dependencies introduce failure modes that must eventually be handled.
- The application will run as a containerized backend service.
- Application instances should not own permanent business state in local memory or local disk.
- Environment-specific configuration must be externalized.
- Secrets do not belong in source code.
- Git, Pull Requests, automated testing, and CI are part of the delivery workflow.
- Existing logging and metrics platforms are infrastructure; our application must provide useful instrumentation.
- Health checks are production requirements even though they are not Product features.
- The first version is one deployable modular application.
- A modular monolith still requires clear logical boundaries.
- Microservices, Kafka, Redis, and distributed architecture are not justified by the current requirements.
- Existing conventions should be understood before new ones are invented.
- Existing code is useful context, but it should not be copied blindly.
- System ownership and external dependency boundaries should be clear before detailed design begins.
- Requirements and engineering context together form the inputs to technical design.
Next lesson:
Definition of Done
There, we will define what it means for an engineering task to actually be complete—not merely that the code has been written, but that implementation, testing, review, documentation, and delivery quality meet the team's expected standard.
