Joining the Backend Team
Definition of Done
So far, we have:
- understood the Product requirement
- written User Stories and Acceptance Criteria
- established the engineering context
Now we need to answer an important question:
When is an engineering task actually complete?
Is it complete when:
the code has been written?
Or when:
it runs on the developer's machine?
Or when:
the tests pass?
Or when:
the Pull Request is merged?
Or only when:
the change is running successfully in production?
In a real engineering team, the word done needs a shared meaning.
In this lesson, we will establish a Definition of Done, commonly shortened to DoD.
This will act as a shared quality bar throughout the course project.
Why “Done” Is Ambiguous
Imagine an engineer says:
BACKEND-104 is done.
What exactly does that mean?
Possibility:
I finished writing the code.
Or:
The code compiles.
Or:
I manually tested the happy path.
Or:
The Pull Request is open.
Or:
The Pull Request has been merged.
Or:
The change has been deployed successfully.
These are all different states.
If every engineer uses a different interpretation of done, planning and delivery become unreliable.
What Is a Definition of Done?
A Definition of Done is a shared team agreement that answers:
What minimum conditions must be satisfied before a work item can be considered complete?
For a backend task, a team might require:
Implementation completed
Acceptance Criteria satisfied
Relevant automated tests added
Existing tests pass
Code reviewed
Required documentation updated
CI passes
These are not feature-specific behaviours.
They are team-wide quality expectations.
Acceptance Criteria vs Definition of Done
These concepts are easy to confuse.
Suppose the Story is:
As a Customer,
I want to cancel an eligible Order,
so that I can stop an Order I no longer want.
Acceptance Criteria might include:
A Customer may cancel their own UNPAID Order.
A PAID Order cannot be cancelled in v1.
Cancelling an Order restores the Inventory
consumed by that Order.
An already-CANCELLED Order cannot be cancelled again.
These describe:
Feature Behaviour
Now compare that with:
Relevant tests exist.
Code is reviewed.
CI passes.
Documentation is updated where needed.
These describe:
Delivery Quality
So:
Acceptance Criteria
↓
Is the feature behaviour correct?
while:
Definition of Done
↓
Has the work been completed
to the team's quality standard?
Both matter.
A Feature Can Satisfy Acceptance Criteria and Still Not Be Done
Suppose an engineer implements Order cancellation.
The behaviour is correct.
Manual testing works.
Every Acceptance Criterion is satisfied.
But:
there are no automated tests
and:
the Pull Request has not been reviewed
and:
CI is failing
Then the feature behaviour may be correct, but the work is not complete according to the team's quality standard.
That is the value of a Definition of Done.
Definition of Done Is Not a Personal Checklist
A DoD should not depend on an individual engineer's personal preference.
Imagine one engineer says:
I don't usually write integration tests.
while another says:
I consider every feature incomplete
without integration tests.
The resulting quality will be inconsistent.
A shared Definition of Done aligns expectations across the team.
Our Course Project Definition of Done
For the Order Management Backend, we will use a practical baseline.
Not every item applies identically to every ticket, but the default quality bar is:
1. The agreed scope is implemented.
2. Acceptance Criteria are satisfied.
3. Relevant validation and failure behaviour are handled.
4. Relevant automated tests are added or updated.
5. Existing automated tests pass.
6. The project builds successfully.
7. The change follows the project's established
architecture and conventions.
8. Required database migrations are included
when persistence changes.
9. Required API documentation is updated
when API behaviour changes.
10. Required technical documentation is updated
when the change affects documented decisions
or behaviour.
11. The change is reviewed through a Pull Request.
12. Meaningful review feedback is resolved.
13. CI passes.
14. Known risks are understood and acceptable
for the scope of the change.
Later, when we begin shipping to production, some work will require additional criteria such as:
rollout verification
production health checks
migration verification
rollback planning
For now, this is our baseline.
1. The Agreed Scope Is Implemented
This sounds obvious, but it prevents a common problem:
An engineer implements part of a ticket and silently ignores the rest.
Suppose the ticket is:
Allow Administrators to deactivate Products.
Acceptance Criteria include:
Administrator can deactivate a Product.
Deactivated Products cannot be ordered.
Historical Orders remain unchanged.
Suppose the engineer only implements:
product.deactivate();
but Order creation still allows inactive Products.
The code exists.
But the agreed scope is not complete.
Do Not Implement More Than the Agreed Scope Either
The opposite problem is scope creep.
Suppose the ticket is:
Allow Administrators to deactivate Products.
But the engineer also adds:
Product deletion
Product archiving
Scheduled activation
Bulk import
Product version history
Those features may be useful someday.
But they are not part of the current work.
Extra functionality creates:
more code
more tests
more review
more risk
more maintenance
The goal of completing a task is:
Implement the agreed work completely, not expand the Product unnecessarily.
2. Acceptance Criteria Are Satisfied
Acceptance Criteria are the feature-level contract.
Before marking a task complete, the engineer should review each criterion explicitly.
Example:
AC-1
An Order must contain at least one item.
AC-2
Every referenced Product must exist.
AC-3
Quantity must be positive.
AC-4
Requested Inventory must be sufficient.
Completion review:
AC-1 → implemented and tested
AC-2 → implemented and tested
AC-3 → implemented and tested
AC-4 → implemented and tested
This is a simple habit, but it prevents many incomplete implementations.
3. Failure Behaviour Must Be Considered
Implementing only the happy path is not enough.
Suppose the task is:
Integrate Payment provider.
Happy path:
Provider confirms success
↓
Order becomes PAID
But known failure paths might include:
payment declined
provider error
timeout
If these scenarios are part of the agreed scope, they must be handled before the work is Done.
Our principle is:
A backend feature is not complete if only its easiest execution path works.
4. Relevant Automated Tests Must Exist
Testing is a core part of our Definition of Done.
The word:
relevant
is important.
We do not require a test for every method simply because the method exists.
Instead, tests should protect important behaviour and risk.
Examples:
Order total calculation
→ Domain/unit test
Order creation with PostgreSQL
→ Integration test
Customer cannot access another Customer's Order
→ API/security integration test
Payment provider failure behaviour
→ Controlled integration/service-boundary test
The correct test type depends on the problem.
Why Manual Testing Is Not Enough
Manual testing is useful during development.
But if it is the only form of verification, it has serious limitations:
not automatically repeatable
easy to forget
poor regression protection
A feature may work today.
Two months later, another engineer changes the system.
If important behaviour has automated tests, that behaviour can be verified again automatically.
Tests Should Protect Behaviour
Weak testing mindset:
Every class needs a test
because coverage should increase.
Better mindset:
Which behaviour would be expensive
or dangerous to break?
For our project, examples include:
Inventory must remain valid.
Order totals must be correct.
Customer ownership must be enforced.
Failed Payment must not make an Order PAID.
These are strong candidates for automated protection.
5. Existing Tests Must Still Pass
Suppose the tests for the new feature pass.
But an existing test fails.
The work is not Done.
Software changes are not isolated.
A new feature can accidentally break existing behaviour.
Example:
New cancellation logic added
but an existing behaviour now fails.
That is a regression.
Our DoD therefore includes:
Existing automated tests pass.
What Is a Regression?
A regression occurs when a new change breaks behaviour that previously worked.
Example:
Before:
Product browsing works.
After an Inventory refactor:
Product browsing returns no Products.
The new feature may work correctly.
But the system as a whole became worse.
Regression tests help catch this class of failure.
6. The Project Must Build Successfully
Suppose everything appears to work inside the developer's IDE.
But:
./gradlew build
fails.
The work is not complete.
Why?
Because:
Developer IDE state
and:
Reproducible project build
are not the same thing.
The repository should contain everything necessary for a clean environment to build the application.
Clean Builds Matter
A developer's machine may contain:
cached artifacts
generated files
local configuration
previous dependencies
that accidentally hide problems.
CI starts from a much cleaner and more controlled environment.
That is one reason automated build pipelines matter.
7. Project Conventions Must Be Followed
Suppose the established application architecture is:
Handler
↓
UseCase
↓
Domain
↓
Repository
with Service reserved for external or third-party integrations.
Now imagine a new engineer places all business logic directly inside an HTTP Handler.
The feature may work.
Tests may even pass.
But the design becomes inconsistent and harder to maintain.
Our Definition of Done therefore includes:
The change follows the established
project architecture and conventions.
Convention Does Not Mean Blind Copying
Existing patterns matter.
But they are not automatically perfect.
If an existing pattern is clearly problematic, an engineer should raise the issue.
For example:
Validation logic is duplicated everywhere.
That may deserve a design discussion.
But silently introducing an entirely different architecture while implementing an unrelated ticket is also risky.
Consistency and improvement both require judgement.
8. Database Changes Need Migrations
Suppose a feature requires a new database column.
The developer manually executes:
ALTER TABLE orders
ADD COLUMN status TEXT;
on their local machine.
The application works locally.
But no migration file exists.
Other environments now have no reproducible way to reach the required schema.
Therefore:
Any persistent schema change must include the corresponding versioned Flyway migration.
Application Code and Schema Must Move Together
Imagine:
New application version expects orders.status
but the target database does not contain that column.
The result is application failure.
Versioned migrations allow application evolution and database evolution to move together in a controlled way.
9. API Changes Need Documentation
Suppose we add:
POST /api/v1/orders
but the API contract is not documented.
How should another client know:
request shape
response shape
validation rules
possible errors
Later, we will use OpenAPI for the public API contract.
When API behaviour changes, API documentation should remain consistent with that behaviour.
Documentation Should Be Relevant
Our DoD says:
Documentation updated where needed.
That does not mean rewriting the README for every ticket.
Documentation should match the change.
For example:
API Behaviour Change
Update the API contract.
Important Architecture Decision Change
Update or replace the relevant ADR/RFC when appropriate.
Deployment Configuration Change
Update operational documentation where necessary.
Pure Internal Refactor
No documentation update may be required.
Documentation Is Part of the System
A common mistake is thinking:
Code is the real work.
Documentation is optional.
Some information is difficult to reconstruct from code alone.
For example:
Why did we choose this transaction boundary?
Why do we not physically delete Products?
Why is automatic retry disabled
for a particular Payment operation?
That reasoning may belong in:
ADR
RFC
Pull Request description
depending on the size of the decision.
10. Technical Decisions Must Stay Consistent
Suppose an existing ADR says:
Products are not physically deleted
because historical Orders must remain valid.
A new feature introduces Product deletion without revisiting that decision.
The change may now conflict with the system's documented design.
Before considering the work Done, the engineer should check whether relevant architectural decisions are still being respected.
11. Pull Request Review Is Required
Our simulated engineering workflow does not use direct unreviewed merges.
Changes go through a Pull Request.
Reviewers can ask:
Does the behaviour match the requirement?
Are edge cases handled?
Is the design understandable?
Are tests meaningful?
Are there security concerns?
Are there migration risks?
Is the scope larger than necessary?
A reviewer is not merely a syntax checker.
Code Review Is Not About Proving Who Is Smarter
A healthy review aims to:
improve correctness
share context
identify risk
maintain consistency
Unhealthy review culture focuses on:
personal preference battles
low-value nitpicking
showing superiority
We will cover code review in more depth later.
For now:
Review is part of our Definition of Done.
12. Review Feedback Must Be Resolved
A Pull Request receiving comments does not make it complete.
Suppose a reviewer writes:
Inventory is reduced before Order persistence.
What happens if persistence fails?
That concern cannot simply be ignored.
Meaningful feedback should result in one of the following:
code changed
or:
discussion confirms the existing design is correct
or:
a deliberate follow-up is agreed
when the issue is not required for current correctness
Unresolved meaningful review feedback is incompatible with Done.
13. CI Must Pass
Our Continuous Integration pipeline will automatically verify at least:
build
automated tests
Later, additional checks may become useful.
But we will not add pipeline complexity simply for appearance.
The baseline is simple:
CI is red
→ work is not Done
even when:
it works on my machine
Why CI Is More Trustworthy Than One Developer Environment
Every developer machine is slightly different.
CI gives us a standardized environment.
If:
Developer A
Developer B
CI
can all build and test the same source successfully, confidence increases.
14. Known Risks Must Be Acceptable
Sometimes the code is correct and every test passes, but the change still has operational risk.
Examples:
A migration rewrites a large table.
Payment behaviour changes.
Authorization rules change.
The deeper DoD question becomes:
Are the known risks understood and appropriately handled?
For a small change:
No special rollout concern.
may be sufficient.
For a risky production change, we may later require:
rollout plan
monitoring plan
rollback plan
The process should match the risk.
Definition of Done Is a Minimum, Not a Maximum
Suppose the DoD says:
Relevant tests added.
Now imagine a ticket changes critical concurrency behaviour.
A single minimal test may not be enough.
The DoD is the baseline.
Engineering judgement still matters.
Definition of Done Should Not Become Bureaucracy
An unhealthy DoD might require:
47 checkboxes
12 mandatory documents
3 approval committees
5 reports
even for a trivial change.
That would make the process more expensive than the work.
A good Definition of Done protects meaningful quality without creating unnecessary ceremony.
Our baseline is intentionally compact.
Different Work May Need Additional Conditions
Consider a small internal refactor.
A sufficient completion bar may be:
behaviour preserved
tests pass
review complete
CI passes
Now consider a database migration.
We may also need:
migration validated
For an external Payment change:
failure behaviour tested
becomes especially important.
For a production deployment change:
rollback considerations
may matter.
So the model is:
One baseline Definition of Done, plus work-specific completion conditions.
Definition of Ready Is Different
Some teams use another concept:
Definition of Ready
It answers:
Is this work sufficiently understood to begin implementation?
Possible conditions:
scope is clear
Acceptance Criteria exist
blocking questions are resolved
We will not introduce a separate formal Definition of Ready in this course.
That would add unnecessary process.
Conceptually, however, the previous lessons have already been doing the same work:
Understand requirement
Clarify behaviour
Define Acceptance Criteria
Understand engineering context
Then we begin planning implementation.
Do Not Confuse “Merged” With “Delivered”
Suppose a Pull Request has been merged.
Has the Customer received the feature?
Not necessarily.
The lifecycle can look like:
Code Complete
↓
Reviewed
↓
Merged
↓
Built
↓
Deployed
↓
Verified
We will explore this in the shipping module.
For the early implementation modules, a merge-ready, reviewed, green-CI change is our baseline completion standard.
For production-specific work, the DoD will expand.
Ticket Statuses Can Reflect Reality
A team may use:
To Do
↓
In Progress
↓
In Review
↓
Done
Another team may also distinguish:
Ready for Deployment
or:
Released
The exact workflow is organization-specific.
The important thing is that a status communicates the real state of the work.
“Code Complete” Can Be a Useful Intermediate State
An engineer might say:
Implementation is complete,
but the ticket is still in review.
That is precise.
It is much better than saying:
It's basically done.
Basically done usually hides important unfinished work.
Example: Order Creation Ticket
Suppose a future ticket is:
BACKEND-125
Implement Order creation workflow
Acceptance Criteria:
Order contains at least one item.
Products must exist.
Quantity must be positive.
Inventory must be sufficient.
Backend calculates the total.
Any invalid item causes
the full request to fail.
Before marking it Done:
[✓] Required behaviour implemented
[✓] Domain/business validations implemented
[✓] Relevant unit tests added
[✓] Integration tests added for the workflow
[✓] Existing tests pass
[✓] Build passes
[✓] No unrelated scope added
[✓] API documentation updated if required
[✓] Pull Request opened
[✓] Review completed
[✓] Feedback resolved
[✓] CI passes
Now the word Done has useful meaning.
Example: Database Migration Ticket
Suppose the ticket is:
Create persistence schema for Products.
Completion may include:
Migration file committed
Application persistence mapping works
Integration tests run against
the expected PostgreSQL schema
A clean environment can apply migrations
Build and tests pass
Review completed
A manually created local table is not enough.
Example: Documentation-Only Ticket
Suppose the ticket is:
Document the Order cancellation design decision.
For this work:
database migration
is irrelevant.
And:
API integration test
may also be irrelevant.
The Definition of Done should not be applied blindly.
Only relevant conditions apply.
Engineering judgement matters.
Example: Bug Fix
Suppose production has a bug:
Order total is calculated incorrectly.
A strong completion standard might include:
root behaviour understood
bug fixed
regression test added
existing tests pass
review complete
CI passes
The important principle is:
A bug fix should ideally prevent the same regression from returning.
It should not merely change one line and move on.
A Useful Question Before Marking Work Done
Ask:
If I disappear after this change is merged, does the repository contain enough code, tests, migrations, and context for the team to safely own it?
If the answer is no, the work is probably not complete.
Ownership Changes the Meaning of Done
Tutorial mindset:
I made it work.
Professional mindset:
The team can now safely own this change.
That difference is significant.
Production code is not personal code.
Once merged, the team collectively owns and maintains it.
“Works Today” vs “Can Be Maintained Tomorrow”
A rushed implementation may work today:
// one large method
// duplicated logic
// no tests
// hidden assumptions
But the next change becomes much more difficult.
A Definition of Done helps preserve a minimum level of maintainability.
Definition of Done and Technical Debt
Sometimes a team intentionally accepts a compromise.
For example:
We cannot complete the ideal refactor
before the release.
Then the limitation should be explicit.
A reasonable approach might be:
Current change is safe for release.
Known limitation is documented.
Follow-up ticket is created.
But a follow-up ticket cannot be used to skip required correctness.
A Follow-Up Ticket Is Not an Escape Hatch
Bad:
Authorization validation is missing.
We'll create a follow-up.
If Authorization is required for the feature to be correct, the ticket is not Done.
Reasonable follow-up:
The current API satisfies the agreed requirement.
Additional performance optimization
would be useful but is not required for v1.
The distinction is:
Required Correctness
versus:
Future Improvement
Quality Is Cheaper Before Merge
A defect is cheapest to catch while coding.
Then it becomes progressively more expensive to discover during:
automated testing
then:
code review
then:
non-production deployment
and eventually:
production
By the time a problem becomes a production incident, it may involve:
Customer impact
investigation
rollback
hotfix
operational coordination
The Definition of Done creates quality gates earlier in the lifecycle.
Review the Diff, Not Just the Result
Suppose all Acceptance Criteria are satisfied.
But the implementation also:
changes unrelated files
or:
removes validation elsewhere
or:
adds unnecessary dependencies
The Pull Request review should catch this.
Small, focused changes are easier to understand and safer to review.
Small Work Items Make “Done” Easier to Understand
Consider this ticket:
Build the entire ordering system.
Asking:
Is it done?
is difficult.
Now compare:
Implement Product creation API.
The boundary is much clearer.
This is one reason our next lesson will break the project into smaller engineering tickets.
Definition of Done Helps Planning
Suppose an engineer estimates:
This feature is one day of coding.
But completion also requires:
implementation
tests
review iteration
migration
documentation
Then the actual work may be larger than the coding effort alone.
Definition of Done helps teams plan the full cost of delivery rather than only the time spent typing code.
Definition of Done Builds Team Trust
If everyone knows:
Done means tested, reviewed,
and CI is green.
then teammates can trust status updates.
They do not need to repeatedly ask:
Did you add tests?
Does it build?
Did anyone review it?
A shared standard reduces communication overhead.
Our Definition of Done Document
We can capture the baseline in a lightweight repository document:
# Definition of Done
A work item is considered complete when all relevant
conditions below are met.
## Behaviour
- Agreed scope is implemented.
- Acceptance Criteria are satisfied.
- Relevant validation and failure behaviour is handled.
## Quality
- Relevant automated tests are added or updated.
- Existing automated tests pass.
- The application builds successfully.
- The change follows established project conventions.
## Data and API Changes
- Required database migrations are included.
- API documentation is updated when API behaviour changes.
- Relevant technical documentation is updated when needed.
## Review
- The change is submitted through a Pull Request.
- Meaningful review feedback is resolved.
- CI passes.
## Risk
- Known risks are understood and acceptable
for the scope of the change.
This will be our baseline throughout the project.
What We Deliberately Did Not Add
Our Definition of Done does not require:
multiple mandatory approvals
performance benchmarks for every ticket
an ADR for every change
production deployment for every code task
an arbitrary code-coverage percentage
because those requirements are not justified by the current context.
Why We Are Not Setting a Code Coverage Percentage
A rule such as:
80% code coverage required
is easy to measure.
But coverage does not guarantee test quality.
A test can execute code without meaningfully verifying behaviour.
For example:
createOrderUseCase.execute(command);
with no meaningful assertion may increase line coverage without increasing confidence.
Our approach is:
Test important behaviour and risk deliberately.
Coverage may later be a useful signal.
It is not our Definition of Done.
Why We Are Not Requiring an RFC for Every Ticket
RFCs are useful when:
design scope is large
or:
a decision is significant
But a ticket like:
Add quantity validation
does not need a ten-page RFC.
Our documentation principle remains:
Ticket
→ bounded implementation work
ADR
→ important architecture decision
RFC
→ larger technical proposal
Use the lightest mechanism that preserves the necessary context.
Why the Definition of Done Will Evolve
As the system and team mature, the DoD may change.
Later, we may discover that:
deployment verification
must become mandatory
or:
migration rollback planning
needs explicit attention
or:
certain security checks
should become standardized
The team can then update the shared baseline.
A Definition of Done is not an immutable law.
It is an engineering agreement.
The DoD Should Reflect Actual Practice
One of the worst situations is when documentation says:
All changes require automated tests.
while reality is:
Half the team skips them.
Then the Definition of Done is fiction.
A smaller standard that the team actually enforces is better than a huge checklist that nobody follows.
Throughout this course, we will apply the established DoD consistently.
Completion Review
Before marking a ticket Done, an engineer can perform a quick self-review:
Did I implement only the agreed scope?
Did I satisfy every Acceptance Criterion?
Did I consider meaningful failure paths?
Are important behaviours protected by tests?
Did I accidentally break existing behaviour?
Does the project build cleanly?
Are migrations and documentation included where needed?
Is the change understandable to the reviewer?
Have meaningful review comments been resolved?
Is CI green?
Are there known risks I have not communicated?
This is a practical habit.
Definition of Done and Production Ownership
Later in the course, the application will be running in production.
At that stage, completion may also require:
Deployment completed
Health checks healthy
Expected metrics look normal
No obvious error-rate regression
Database migration succeeded
We will add those delivery criteria when they become relevant.
For now, we are establishing the implementation-workflow baseline.
Engineering Principle
The core principle from this lesson is:
Code written is not the same as work completed.
Professional completion means:
correct behaviour
+
appropriate tests
+
review
+
reproducible build
+
required documentation
+
understood risk
And the most important mindset is:
A change is Done when the team can safely own it, not merely when the author stops typing.
Where We Are Now
Our Module 1 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
The Product behaviour is sufficiently clear.
The engineering environment is clear.
The quality bar is also clear.
We can now begin planning implementation work.
Summary
In this lesson, we learned that:
Doneneeds a shared team-wide meaning.- A Definition of Done is a minimum quality bar.
- Acceptance Criteria define feature behaviour.
- Definition of Done defines delivery quality.
- Writing the code does not mean the work is complete.
- Relevant automated tests are part of completion.
- Existing tests must continue to pass.
- The project must build successfully in a reproducible environment.
- New work should follow the project's established architecture and conventions.
- Persistent schema changes require versioned Flyway migrations.
- API documentation should be updated when API behaviour changes.
- Technical documentation should be updated when relevant design decisions change.
- Pull Request review is part of the workflow.
- Meaningful review feedback must be resolved.
- CI must be green.
- Known risks must be understood and communicated.
- The DoD applies according to the nature of the work rather than as a blind checklist.
- We are not adding arbitrary coverage targets, mandatory RFCs, or unnecessary approval processes.
- A follow-up ticket cannot be used to skip required correctness.
- Quality issues are cheaper to catch before merge than in production.
- Small work items make completion easier to evaluate.
- A professional change becomes team-owned software, not author-owned code.
Next lesson:
Creating Our Initial Engineering Backlog
There, we will use the Product Stories, Acceptance Criteria, engineering context, and Definition of Done to create the project's first realistic engineering tickets, identify dependencies, order the work, and prepare the project for implementation.
