Joining the Backend Team

Welcome to the Engineering Team

LuettavaEsikatselu

Katselet ilmaista esikatseluoppituntia.

In this course, we are not going to learn Java or Spring Boot features through a collection of isolated examples.

Instead, we will build a real-world backend application and evolve that same application throughout the course.

You are not just a learner here.

From this moment on, imagine that you have joined a software engineering team as a new Backend Engineer.

Your team is about to build a new Order Management Backend.

The system will allow customers to:

  • browse products
  • place orders
  • make payments
  • view their order history
  • cancel eligible orders

Administrative users will be able to:

  • create and update products
  • deactivate products
  • manage inventory
  • view orders

But we are not going to start by writing code.

Professional software development usually does not begin like this:

New Project
    ↓
Open IDE
    ↓
Start Coding

A more realistic process looks like this:

Business Problem
      ↓
Requirements
      ↓
Technical Discussion
      ↓
Design
      ↓
Engineering Tasks
      ↓
Implementation
      ↓
Testing
      ↓
Review
      ↓
Deployment
      ↓
Operation

The purpose of this course is to let you experience that entire process in practice.


The Project We Are Joining

Imagine that we work for a commerce company.

The company already sells products, but now it wants a dedicated backend system for its ordering process.

Our team's responsibility is to build that backend.

The initial version will support several core capabilities:

  • product management
  • inventory management
  • order creation
  • payment processing
  • order history
  • order cancellation
  • administrative operations

Customer identity will come from the application's authentication mechanism. We are not building a separate customer-account management subsystem as part of this project.

As the course progresses, we will implement these capabilities gradually.

But keep one important idea in mind:

We are not going to build the entire system at once.

Professional software is usually delivered through small, reviewable, incremental changes.

Our project will evolve the same way.


Your Role

In this project, you are a Backend Engineer.

That means your job is not limited to writing APIs.

A backend engineer may need to:

  • understand product requirements
  • identify ambiguous requirements
  • ask clarifying questions
  • design technical solutions
  • define API contracts
  • design database models
  • implement business logic
  • write tests
  • review another engineer's code
  • explain technical changes
  • investigate production failures
  • modify an existing system safely
  • document technical decisions

Throughout the course, we will practise different parts of these responsibilities.


Software Development Is More Than Writing Code

When learning programming, we often work with a simplified model:

Problem
   ↓
Code
   ↓
Output

That model is useful for small programming exercises.

Production software is different.

There are many important decisions between the problem and the code.

Suppose a Product Manager says:

A customer should be able to place an order.

That sounds simple.

But as backend engineers, we should immediately start asking questions.

For example:

Can an order contain multiple products?

Can the same product appear more than once?

What happens if inventory is insufficient?

When should inventory decrease?

Can a customer cancel an order?

Can an unpaid order be cancelled?

Can a paid order be cancelled?

What happens if payment succeeds but our database update fails?

How do we calculate the final price?

Should the price come from the current product price?

What happens if the product price changes later?

This is one of the major differences between software engineering and simply writing code.

Writing the code is often the most straightforward part.

The difficult part is:

Understanding exactly which problem needs to be solved and how the system should behave.


We Will Not Start with Spring Boot

This is a Java backend development course.

Even so, the first few lessons will contain very little Spring Boot code.

That is intentional.

A framework is a tool.

Spring Boot gives us capabilities such as:

  • HTTP controllers
  • dependency injection
  • configuration
  • database integration
  • validation
  • application lifecycle management

But Spring Boot cannot decide whether:

  • the requirement has been understood correctly
  • a responsibility belongs in the right component
  • an API contract is well designed
  • a business invariant has been identified
  • a transaction is required
  • a failure scenario has been handled correctly
  • a change is risky
  • a production incident has been investigated properly

Learning the framework is important.

But knowing a framework and being a backend engineer are not the same thing.

Throughout this course, we will develop both:

Backend Engineering Thinking
            +
Java / Spring Boot Implementation

Our Engineering Workflow

Throughout the course, we will follow a repeatable engineering workflow.

A feature will usually move through a process similar to this:

Requirement
    ↓
Understand the Problem
    ↓
Clarify Ambiguity
    ↓
Technical Design
    ↓
RFC / ADR when needed
    ↓
Break Work into Tickets
    ↓
Implementation
    ↓
Testing
    ↓
Pull Request
    ↓
Code Review
    ↓
Merge
    ↓
Deploy
    ↓
Observe

Not every small change needs a full RFC.

That is also an important engineering lesson.

Documentation should not exist to create bureaucracy.

Its purpose is to improve thinking and communication where those things are valuable.

Later, we will learn when:

  • a short engineering ticket is enough
  • an ADR is appropriate
  • a larger RFC is worth writing

What Is an RFC?

You will see the term RFC throughout this course.

RFC commonly stands for Request for Comments.

In a software engineering team, an RFC can be a document in which an engineer explains a technical problem and proposes a solution so that other engineers can review it before implementation.

An RFC might contain sections such as:

Problem

Context

Goals

Non-Goals

Proposed Design

Alternatives

Risks

Rollout Plan

Not every company uses the same format.

Some teams call these documents:

RFC

Design Document

Technical Proposal

Engineering Proposal

The name is less important than the purpose.

The important idea is:

Think about and discuss important technical decisions in a structured way before hiding those decisions inside code.

We will write RFCs for our Order Management Backend when the problem justifies one.


What Is an ADR?

Another term we will use is:

ADR — Architecture Decision Record

An ADR records a specific technical decision and the reasoning behind it.

For example:

Why are we using PostgreSQL?

Why are we using database migrations?

Why did we choose a particular API error format?

An RFC may describe a larger technical proposal.

An ADR usually focuses on preserving one important decision and its reasoning.

Months later, when another engineer joins the project, they should not have to reverse-engineer the reason behind every architectural choice by reading code.

They should be able to read the relevant ADR and understand the context.


Engineering Tickets

After designing a solution, we will break the work into smaller engineering tasks.

For example:

BACKEND-101 Bootstrap Order Management Backend

BACKEND-102 Create Product domain model

BACKEND-103 Add Product persistence

BACKEND-104 Expose Product API

BACKEND-105 Implement Order creation

A useful ticket should not simply say:

Build orders.

It should provide enough context to understand the expected behaviour.

Later, we will work in more detail with:

  • user stories
  • acceptance criteria
  • Definition of Done

Why Tickets Matter

Tickets are not merely project-management tracking tools.

A well-written ticket creates a useful boundary around engineering work.

Consider this ticket:

Implement ordering system

That scope is so large that an engineer may not even know where to start.

Compare it with:

BACKEND-121

Create an order for an authenticated customer.

Acceptance Criteria:

- Order must contain at least one item.
- Every referenced product must exist.
- Requested quantity must be available.
- Order total must be calculated from server-side product prices.
- Invalid requests must return an appropriate API error.

The problem is now much clearer.

Smaller engineering work is not only easier to manage.

It also makes:

  • review easier
  • testing easier
  • risk smaller
  • rollback easier
  • progress easier to understand
  • parallel work easier

Pull Requests Are Communication

Writing the code does not mean the work is finished.

In a professional engineering team, a change usually goes through review.

We may create a Pull Request, commonly called a PR.

A useful PR description might look like this:

## What

Adds order cancellation support.

## Why

Customers need to cancel unpaid orders.

## Changes

- Added cancellation domain logic
- Restores consumed inventory
- Added cancellation API
- Added integration tests

## Testing

- Unit tests
- API integration tests

## Risks

Inventory restoration happens inside the order transaction.

A PR is not just a container for code.

It is communication with the reviewer.

A reviewer should be able to understand quickly:

  • what changed
  • why it changed
  • how it was tested
  • where the risks are

This is an extremely important skill in a real engineering career.


Production Changes Everything

Running an application successfully on a local machine is not the end of software development.

Production systems introduce additional questions:

How will we know if the service is healthy?

What happens when the database is unavailable?

What happens if an external payment API times out?

How do we find a failed request?

What happens during deployment?

Can a database migration break existing application instances?

Can the same payment request be processed twice?

What happens if two customers try to buy the final item simultaneously?

Questions like these are what make backend engineering interesting.

We do not need to know every answer at the beginning of the course.

In fact, we should not try to solve every possible future problem immediately.

As the system evolves, new problems will naturally appear.

Then we will learn and apply the appropriate solution.


We Will Make Mistakes

We will not begin the course with a perfectly designed system.

Real software rarely begins with perfect architecture either.

We will make decisions.

Later, new requirements will arrive.

Some assumptions may turn out to be wrong.

Some designs may need to change.

We will even simulate a production incident.

That is not a failure of the course.

That is software development.

Real systems evolve over time.

A strong engineer's skill is not simply the ability to write the correct code on the first attempt.

It is also:

The ability to change an existing system safely.


Our First Version Will Be a Modular Application

Our Order Management Backend will begin as a single backend application.

Conceptually:

Order Management Backend

├── Products
├── Inventory
├── Orders
└── Payments

Customer identity is supplied by the authentication system and used by application workflows, but we are not creating a separate Customer management capability.

At this stage of the course, we will not split the application into unnecessary microservices.

Our current goal is to learn how to build production-quality backend software.

Topics such as:

  • microservices
  • Kafka
  • distributed transactions
  • distributed caching
  • service meshes

become useful only when there is a real problem that requires them.

Architecture complexity is not an achievement by itself.

A system that satisfies its requirements with a simpler architecture is usually a better starting point.


What We Will Build Toward

By the end of the course, our application will be much more than a collection of CRUD endpoints.

We will build a backend containing:

HTTP APIs

Business Logic

Domain Models

Validation

PostgreSQL Persistence

Database Migrations

Transactions

Authentication

Authorization

External Service Integration

Testing

Logging

Metrics

Health Checks

Docker

CI

Deployment Practices

But we will not learn these as an arbitrary checklist.

Each capability will appear because the project creates a reason for it.

For example:

We will learn PostgreSQL because the application needs durable data.

We will learn transactions because Order creation changes multiple pieces of database state that must remain atomic.

We will learn authentication and authorization because one Customer must not be able to access another Customer's Orders.

We will learn timeouts and retries because an external payment provider will not always respond perfectly.

We will learn observability because when something fails in production, engineers need to understand what happened inside the system.

This distinction is important:

Problem first. Technology second.


Your Engineering Repository

As the course progresses, our repository may evolve into something like:

order-management/
├── src/
│   ├── main/
│   └── test/
│
├── docs/
│   ├── rfcs/
│   ├── adr/
│   └── incidents/
│
├── Dockerfile
├── compose.yaml
└── README.md

src/ contains application code.

docs/rfcs/ contains larger technical proposals.

docs/adr/ contains architecture decisions.

docs/incidents/ will later contain production incident reviews.

Engineering context becomes part of the repository alongside the code.


A Backend Engineer Thinks in Behaviour

Throughout this course, we will develop one important habit:

Do not immediately think about implementation when you see a feature request.

Think about behaviour first.

Suppose the requirement says:

Customer can cancel an order.

Do not immediately write:

order.setStatus(
        OrderStatus.CANCELLED
);

First ask:

Which orders can be cancelled?

Can a paid order be cancelled?

Should inventory be restored?

Can the same order be cancelled twice?

Who is allowed to cancel the order?

What response should the API return?

Do we need to notify another system?

Only after understanding the behaviour should we design the implementation.

The order matters:

Behaviour
    ↓
Design
    ↓
Code

Not:

Code
    ↓
Try to figure out behaviour later

Think Like You Just Joined the Team

At this point, we still do not have a complete set of requirements.

All we know is the high-level context:

The company wants to build an Order Management Backend.

An inexperienced developer might already open an IDE and create:

OrderController

We are not going to do that.

Our next responsibility is to understand the business requirement.

We need to discover:

  • who will use the system
  • what they should be able to do
  • which behaviours are mandatory
  • which behaviours are outside this version
  • which constraints exist
  • how success will be defined

Without that information, we might write code quickly and still build the wrong system.


Engineering Principle

Remember this principle from the lesson:

The job is not to write code. The job is to solve the right problem with software.

Code is an important part of that job.

But before code comes understanding.

And after production comes ownership.

A backend engineer participates in the entire lifecycle.


What Comes Next

In the next lesson, our team will receive the actual product requirement for the Order Management Backend.

It will intentionally begin at a high level.

We will read it like engineers and separate:

What is explicitly required?

What are we assuming?

What is ambiguous?

What needs clarification?

What should not be built yet?

That is where the real engineering journey of our Order Management Backend begins.