HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
4 Topics
Chapter•100 Minutes

Java Backend Engineering

Connect Java applications to HTTP services and databases, understand JDBC, transactions, connection pools, REST API design, and backend resilience.

HTTP Client and REST Fundamentals

Modern Java includes an HTTP client API for synchronous and asynchronous HTTP calls.

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://example.com/api/users"))
    .GET()
    .build();

HttpResponse<String> response =
    client.send(request, HttpResponse.BodyHandlers.ofString());

A production HTTP client needs timeouts, connection reuse, retries where safe, observability, response validation, and protection against unbounded concurrency.

Key Takeaways
  • HTTP clients need timeouts and resilience policies.
  • Retries are only safe when operation and server semantics allow them.
  • API contracts should make validation and error behavior explicit.

JDBC and Connection Pools

JDBC provides a standard API for relational database access. A connection pool reuses database connections rather than creating a new connection for every request.

try (Connection connection = dataSource.getConnection();
     PreparedStatement statement =
         connection.prepareStatement(
             "select id, name from users where id = ?")) {

    statement.setString(1, userId);

    try (ResultSet rs = statement.executeQuery()) {
        if (rs.next()) {
            return rs.getString("name");
        }
    }
}

Always use parameterized queries. Pool size should account for database capacity and query latency; making it huge can overload the database.

SQL injection

Never concatenate untrusted input into SQL. Use prepared statements or a safe data-access abstraction.

Key Takeaways
  • Use prepared statements for values.
  • Connection pools are shared scarce resources.
  • Database pool sizing must account for database capacity.

Transactions and Isolation

A transaction groups database operations into an atomic unit. Isolation controls how concurrent transactions observe each other's changes.

connection.setAutoCommit(false);

try {
    debitAccount(connection, fromId, amount);
    creditAccount(connection, toId, amount);
    connection.commit();
} catch (Exception e) {
    connection.rollback();
    throw e;
}

Transaction boundaries should usually live at a service/use-case layer rather than being scattered across repository methods.

Key Takeaways
  • Transactions define atomicity boundaries.
  • Isolation is a concurrency trade-off.
  • Keep transaction boundaries aligned with business operations.

Timeouts, Retries, Bulkheads, and Backpressure

A service is only as reliable as its dependencies allow. A slow database or API can consume all request resources if there are no timeouts or concurrency limits.

Resilience techniques include deadlines, bounded retries with jitter, circuit breakers, bulkheads, rate limits, queues, and backpressure.

Request
  |
  v
Timeout --> Retry policy --> Downstream
  |              |
  |              +--> bounded attempts
  v
Fallback / error

Observability should include latency, error rate, saturation, dependency timings, and retry counts.

Key Takeaways
  • Timeouts are mandatory for remote dependencies.
  • Retries can amplify outages if uncontrolled.
  • Backpressure and concurrency limits protect downstream systems.