Series MapLesson 97 / 112
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Final StretchOrdered learning track

Contract, Performance, and CI Test Strategy

Contract Testing Performance Benchmarking CI and Flaky Diagnosis

Test strategy for API compatibility, event compatibility, performance confidence, benchmarking discipline, CI quality gates, and flaky test diagnosis in enterprise JAX-RS services

6 min read1086 words
PrevNext
Lesson 97112 lesson track93–112 Final Stretch
#contract-testing#performance-testing#benchmarking#ci+5 more

Part 097 — Contract Testing, Performance Testing, Benchmarking, CI, and Flaky Diagnosis

Fokus part ini: memahami test strategy yang menjaga compatibility dan production confidence. Unit test dan integration test membuktikan behavior lokal. Contract test, performance test, benchmark, dan CI quality gate membuktikan bahwa perubahan masih aman untuk consumer, dependency, runtime, dan production operation.

Catatan penting:

This part does not assume CSG uses Pact, Spring Cloud Contract, REST Assured,
OpenAPI diff tools, AsyncAPI tooling, k6, Gatling, JMeter, JMH, GitHub Actions,
Jenkins, GitLab CI, Azure DevOps, Buildkite, or any specific internal quality gate.

Treat all tooling, CI stages, performance baselines, contract ownership, and
release gates as internal verification items.

Di sistem enterprise, bug paling mahal sering bukan bug unit kecil.

Bug paling mahal biasanya adalah:

breaking API change
breaking event schema change
hidden performance regression
slow query under production cardinality
retry storm under dependency failure
flaky CI hiding real failures
benchmark result interpreted incorrectly
load test that proves nothing useful

Test strategy senior harus menjawab:

What risk does this test reduce?
At which boundary does the risk exist?
Who is protected by this test?
What failure should block merge/release?
What failure should trigger investigation but not block?

1. Mental Model: Tests as Risk Controls

Test bukan tujuan. Test adalah risk control.

flowchart TD Change[Code / Schema / Config Change] Risk[Risk Introduced] Test[Test Control] Gate[CI / Release Gate] Production[Production] Change --> Risk Risk --> Test Test --> Gate Gate --> Production

Setiap test harus punya alasan.

Unit test:
  Does this function/class behave correctly?

Integration test:
  Do real components work together?

Contract test:
  Did we preserve producer/consumer agreement?

Performance test:
  Does this survive realistic load and data shape?

Benchmark:
  Is this implementation faster/slower under controlled micro conditions?

CI gate:
  Should this change be allowed to merge or deploy?

Weak test strategy biasanya terlihat seperti ini:

many tests
unclear risk coverage
slow pipeline
flaky failures
ignored failures
no compatibility gate
no performance baseline
no ownership of failures

Strong test strategy terlihat seperti ini:

small number of high-signal tests
clear boundary coverage
fast feedback for common changes
slow tests isolated into proper stages
compatibility checked before release
performance regressions visible
flaky tests aggressively triaged

2. Contract Testing: Why It Exists

Contract testing melindungi integrasi.

Dalam sistem enterprise, satu service jarang berdiri sendiri.

JAX-RS API is consumed by UI, other services, partners, batch jobs, or workflow engines.
Kafka events are consumed by downstream services, analytics, reconciliation jobs, or audit systems.
gRPC/protobuf APIs may be consumed by generated clients.

Contract test memastikan producer dan consumer tidak diam-diam berubah arah.

flowchart LR Provider[Provider Service] Contract[Contract Artifact] ConsumerA[Consumer A] ConsumerB[Consumer B] ConsumerC[Consumer C] Provider --> Contract Contract --> ConsumerA Contract --> ConsumerB Contract --> ConsumerC

Tanpa contract test:

Provider changes field type.
Consumer deploys later.
Production breaks after release.
The team that caused the break may not see it in its own tests.

Dengan contract test:

Breaking change is detected before merge or before release.

3. Contract Types

Contract typeBoundary protectedExample artifact
HTTP provider contractJAX-RS API response/request compatibilityOpenAPI
HTTP consumer contractConsumer expectation against providerPact-like contract
Event producer contractKafka/Rabbit event schemaAvro/JSON Schema/Protobuf
Event consumer contractConsumer tolerance to producer evolutionsample events / schema tests
gRPC contractRPC method and message compatibilityprotobuf
Error contractFailure response shape and code semanticsOpenAPI + examples
Operational contractTimeout, retry, idempotency expectationsdocumentation + tests

Senior engineer harus peduli bukan hanya “schema valid”, tetapi “consumer masih aman”.


4. OpenAPI Contract Testing

Untuk HTTP/JAX-RS API, OpenAPI sering menjadi kontrak utama.

Yang harus diuji:

path exists
method exists
required request fields unchanged
required response fields unchanged
status codes documented
error shape stable
headers documented
content type stable
pagination shape stable
nullable/optional semantics stable

Contoh breaking change:

- GET /quotes/{id} returns 200 with field "quoteId"
+ GET /quotes/{id} returns 200 with field "id"

Untuk internal Java code, breaking change bisa terlihat kecil:

// Before
public record QuoteResponse(String quoteId, String status) {}

// After
public record QuoteResponse(String id, String status) {}

Tetapi untuk consumer, ini contract break.

Checklist OpenAPI diff:

removed path
removed method
removed request field accepted by old clients
new required request field
removed response field used by clients
changed field type
changed enum values without compatibility policy
changed status code semantics
changed auth requirement
changed pagination structure
changed error envelope
changed content type

5. Consumer-Driven Contract Testing

Consumer-driven contract testing berguna ketika consumer expectation lebih spesifik daripada OpenAPI.

Misalnya UI atau downstream service hanya peduli subset field.

sequenceDiagram participant Consumer participant ContractBroker participant Provider Consumer->>ContractBroker: publish expected interaction Provider->>ContractBroker: fetch contracts Provider->>Provider: verify provider behavior Provider-->>ContractBroker: publish verification result

Contoh consumer expectation:

{
  "request": {
    "method": "GET",
    "path": "/quotes/Q-123"
  },
  "response": {
    "status": 200,
    "body": {
      "quoteId": "Q-123",
      "status": "DRAFT"
    }
  }
}

Keuntungan:

consumer expresses what it actually needs
provider can verify before release
less reliance on tribal knowledge

Risiko:

too many brittle contracts
contracts coupled to incidental fields
stale consumer contracts
no ownership when contract fails

Senior review question:

Is this contract protecting a real dependency, or just duplicating implementation detail?

6. Event Contract Testing

Event-driven systems membutuhkan contract testing lebih disiplin daripada HTTP karena consumer sering asinkron dan tidak terlihat langsung saat release.

Event contract harus menguji:

topic/event name
schema compatibility
required fields
optional fields
field type
semantic meaning
event key
ordering assumption
idempotency key
event version
deprecation policy
replay compatibility

Contoh event:

{
  "eventId": "evt-123",
  "eventType": "QuoteSubmitted",
  "eventVersion": 2,
  "occurredAt": "2026-07-10T10:15:30Z",
  "tenantId": "tenant-a",
  "quoteId": "Q-123",
  "submittedBy": "user-456"
}

Breaking event changes:

remove quoteId
change quoteId from string to object
rename occurredAt to timestamp
change event key from quoteId to tenantId
change event meaning without version bump
emit duplicate semantic events without consumer policy
remove old enum value consumer still handles

Contract test untuk event harus melindungi dua sisi:

producer compatibility:
  can producer still emit schema-valid event?

consumer compatibility:
  can consumer still process old and new acceptable events?

7. Compatibility Matrix

Contract compatibility harus divisualkan sebagai matrix.

               Consumer v1   Consumer v2   Consumer v3
Provider v1       OK            OK?           ?
Provider v2       OK?           OK            OK?
Provider v3       BREAK?        OK?           OK

Untuk enterprise rollout, compatibility matrix penting karena deployment tidak selalu serentak.

blue-green deployment
canary deployment
multi-region rollout
customer-specific rollout
tenant-specific rollout
on-prem delayed upgrade
partner integration delayed upgrade

Rule praktis:

If provider and consumer cannot be deployed atomically, compatibility is not optional.

8. Performance Testing: What It Actually Proves

Performance testing bukan sekadar “berapa RPS?”.

Performance testing harus menjawab:

Does the service meet latency target under expected load?
Where does it saturate?
What is the bottleneck?
How does it fail under overload?
Does it recover after overload?
Does it respect downstream limits?

Jenis test:

TestPurpose
Smoke performance testBasic latency sanity
Load testExpected production load
Stress testFind saturation point
Spike testSudden traffic jump
Soak testLong-running stability/leak detection
Breakpoint testFind maximum sustainable capacity
Failover testBehavior during dependency/platform failure

Performance test tanpa hypothesis sering membuang waktu.

Good hypothesis:

Under 150 RPS with p95 payload size and production-like DB cardinality,
GET /quotes/{id} should remain below 200ms p95 and error rate below 0.1%.

Bad hypothesis:

Let's run 1000 users and see what happens.

9. Performance Test Targets

Target harus jelas.

p50 latency
p95 latency
p99 latency
error rate
throughput
CPU utilization
memory utilization
GC pause
DB connection pool usage
DB query latency
Kafka publish latency
consumer lag
Redis latency
HTTP downstream latency
queue depth
thread pool saturation

Contoh SLO-like target:

GET /quotes/{id}
  p95 latency < 200ms
  p99 latency < 500ms
  5xx rate < 0.1%
  timeout rate < 0.05%

POST /quotes/{id}/submit
  p95 latency < 800ms
  5xx rate < 0.2%
  duplicate submission prevented
  event publish succeeds or outbox records pending event

Penting:

Latency target tanpa payload shape dan data cardinality tidak lengkap.

10. Production-Like Data Shape

Performance test sering gagal sebagai signal karena data test tidak mirip production.

Data shape yang harus dipikirkan:

number of tenants
number of quotes per tenant
number of quote lines
catalog size
pricing rule complexity
order history size
index selectivity
payload size distribution
large customer edge cases
archived vs active data ratio

Untuk CPQ/order-style system, satu “quote” bisa sangat kecil atau sangat besar.

Quote A:
  1 line item
  no discount
  one-time charge only

Quote B:
  2,000 line items
  nested bundles
  discounts
  tax rules
  effective dates
  multi-currency

Keduanya bukan workload yang sama.


11. Benchmarking vs Performance Testing

Benchmark berbeda dari performance test.

Benchmark mengukur bagian kecil dalam kondisi terkontrol.

Performance test mengukur sistem lebih besar dalam kondisi realistis.

AspectBenchmarkPerformance test
ScopeMethod/component kecilService/system
ToolingJMH-likek6/Gatling/JMeter-like
GoalCompare implementationValidate capacity/latency
EnvironmentControlledProduction-like
RiskMisleading micro resultExpensive and noisy

Benchmark berguna untuk:

serialization strategy
mapping strategy
regex/parser
pricing algorithm hot path
cache key generation
hashing/signature operation

Benchmark tidak membuktikan:

real endpoint capacity
DB performance
network latency
gateway behavior
GC behavior under full service load

12. JMH-Style Benchmark Discipline

Java microbenchmark rentan salah karena JIT, dead-code elimination, warmup, allocation, dan measurement noise.

Checklist benchmark:

warmup configured
measurement iterations configured
forks configured
blackhole used when needed
inputs realistic enough
allocation measured
GC considered
CPU frequency noise considered
result compared with confidence

Contoh benchmark target yang masuk akal:

Compare MapStruct mapper vs manual mapper for 10,000 DTO conversions.
Compare JSON serialization config A vs B for representative payload.
Compare pricing rule evaluation strategy for 100/1,000/10,000 line items.

Anti-pattern:

Benchmark one endpoint by calling localhost once.

Itu bukan benchmark. Itu anecdote.


13. CI Test Strategy

CI pipeline harus memberi feedback berlapis.

flowchart TD Commit[Commit / PR] Fast[Fast Checks] Unit[Unit Tests] Contract[Contract Checks] Integration[Integration Tests] Security[Security/SCA/SBOM] Perf[Performance Smoke] Release[Release Candidate] Commit --> Fast Fast --> Unit Unit --> Contract Contract --> Integration Integration --> Security Security --> Perf Perf --> Release

Contoh stage:

Stage 1: format/lint/static checks
Stage 2: unit tests
Stage 3: API/event contract checks
Stage 4: integration tests with containers
Stage 5: migration validation
Stage 6: security/SCA/SBOM/image scan
Stage 7: performance smoke or nightly performance test
Stage 8: packaging and artifact publishing

Tidak semua test harus jalan di setiap commit.

Tapi setiap risk penting harus punya gate di tempat yang tepat.


14. PR vs Nightly vs Release Gates

GateShould runPurpose
PR fast gatefast static + unit + contract diffprotect main branch
PR integration gateselected integration testscatch wiring/schema/runtime break
Nightly gatefull integration + performance + longer scenarioscatch expensive failures
Release gatecompatibility + migration + security + smoke perfprotect production
Post-deploy gatesmoke + synthetic + dashboardsdetect deployment/runtime issue

Prinsip:

Fast feedback for common mistakes.
Deep feedback for expensive risks.
Release feedback for production compatibility.

15. Flaky Test Diagnosis

Flaky test adalah test yang kadang pass, kadang fail tanpa perubahan code yang relevan.

Flaky test merusak trust CI.

Failure mode:

real failures ignored because "CI is flaky"
engineers rerun instead of diagnose
pipeline cost increases
merge discipline collapses
production bugs escape

Root cause umum:

time-dependent assertion
random port conflict
shared database state
shared Kafka topic/consumer group
non-isolated Redis key
async wait with fixed sleep
race condition
ordering assumption
external dependency
clock/timezone dependency
resource leak between tests
parallel test interference
container startup race

16. Flaky Test Debugging Framework

Gunakan struktur ini:

1. Is the failure deterministic locally?
2. Does it only fail under parallel execution?
3. Does it depend on time/order/randomness?
4. Does it share external state?
5. Does it assume async work completed too early?
6. Does it depend on slow CI hardware?
7. Does it leak resources from prior tests?
8. Does it depend on timezone/locale/environment variable?

Contoh bad async test:

service.submitQuote("Q-123");
Thread.sleep(500);
assertThat(repository.findStatus("Q-123")).isEqualTo("SUBMITTED");

Lebih baik:

service.submitQuote("Q-123");

await()
    .atMost(Duration.ofSeconds(5))
    .untilAsserted(() ->
        assertThat(repository.findStatus("Q-123")).isEqualTo("SUBMITTED")
    );

Catatan:

Even await-based tests must have clear timeout and isolated state.

17. Test Isolation Rules

Aturan isolasi untuk enterprise test:

unique tenant ID per test
unique resource ID per test
unique Kafka topic or consumer group per test suite
unique Redis key prefix per test
transaction rollback only if it matches runtime behavior
clean DB schema or clean fixtures per suite
no dependency on test execution order
no wall-clock assumption without clock abstraction

Untuk PostgreSQL:

use deterministic migration baseline
clean schema intentionally
avoid hidden shared rows
avoid sequence assumptions unless controlled

Untuk Kafka:

avoid shared consumer group across tests
wait for assignment before producing if needed
assert eventually, not immediately
control topic retention if possible

Untuk Redis:

prefix keys by test run
avoid global FLUSHALL in shared environment
assert TTL behavior with bounded tolerance

18. Contract + Performance + CI Review Matrix

Gunakan matrix ini saat review PR.

Change typeRequired evidence
New endpointOpenAPI update, endpoint tests, error contract, auth check
Changed response DTOOpenAPI diff, consumer impact review
Changed eventschema compatibility check, sample event test, consumer inventory
Changed SQL queryintegration test, query plan review if critical
Changed migrationmigration test, expand-contract review, rollback/roll-forward plan
Changed retry configfailure scenario test, retry budget review
Changed thread/executor codecontext propagation test, race/concurrency review
Changed serialization configcompatibility tests, sample payload tests
Changed security dependencyauth negative tests, vulnerability scan
Changed performance-critical codebenchmark or load test evidence

19. PR Review Checklist

Contract Testing
[ ] Does the PR update API/event contracts when behavior changes?
[ ] Is the change backward-compatible?
[ ] Is there an explicit breaking-change decision if not compatible?
[ ] Are error shapes and status codes still compatible?
[ ] Are generated clients/servers updated if used?
[ ] Are OpenAPI/AsyncAPI/protobuf artifacts validated in CI?

Performance
[ ] Could this change affect latency, throughput, memory, DB load, or Kafka lag?
[ ] Is there benchmark/load evidence for performance-critical change?
[ ] Are payload size and data cardinality considered?
[ ] Are slow queries or new indexes reviewed?
[ ] Does the change add blocking calls or unbounded concurrency?

CI
[ ] Does the test run in the right stage?
[ ] Is the test deterministic?
[ ] Is the test isolated from shared state?
[ ] Does the test fail for the right reason?
[ ] Is a flaky failure being hidden by retries?

Flaky Diagnosis
[ ] Are sleeps replaced with bounded awaits?
[ ] Are Kafka/Redis/DB resources isolated?
[ ] Is time controlled with clock abstraction where needed?
[ ] Are parallel test interactions controlled?

20. Internal Verification Checklist

Verify these in the internal CSG codebase/platform:

Contract governance
[ ] Is OpenAPI generated from code, written manually, or source of truth?
[ ] Is AsyncAPI used for Kafka/event documentation?
[ ] Is protobuf/gRPC used anywhere?
[ ] Is there an API linting rule set?
[ ] Is contract diff enforced in CI?
[ ] Is there consumer-driven contract testing?
[ ] Is there a contract broker or central contract repository?
[ ] Are generated clients used internally?
[ ] Who approves breaking API/event changes?

Performance/testing
[ ] What performance test tool is standard?
[ ] Are there endpoint-level latency targets?
[ ] Are there service-level SLOs/SLA/error budgets?
[ ] Is there production-like test data?
[ ] Are nightly load/stress/soak tests run?
[ ] Are performance regressions gated or only reported?
[ ] Is benchmark evidence expected for hot path changes?

CI
[ ] What CI system is used?
[ ] Which tests run on PR vs merge vs nightly vs release?
[ ] Are integration tests container-based?
[ ] Is Docker available in CI?
[ ] Are test results tracked over time?
[ ] Are flaky tests tagged/quarantined/owned?
[ ] Is rerun policy documented?

Release
[ ] What tests block release?
[ ] Are contract compatibility checks part of release gate?
[ ] Are migration checks part of release gate?
[ ] Is smoke performance run before or after deployment?

21. Senior Engineer Heuristics

A test that no one trusts is operational debt.
A contract that is not enforced is documentation, not governance.
A benchmark without warmup is suspicious.
A load test without production-like data shape is weak evidence.
A flaky test should be treated like a production reliability issue.
A CI gate should be tied to a risk, not to tradition.
A compatibility matrix is more useful than a generic claim of backward compatibility.

Senior-level question:

What failure would this test catch before production, and is that failure worth the cost of the test?

22. Summary

Contract testing protects compatibility.

Performance testing protects capacity and latency assumptions.

Benchmarking protects micro-level implementation choices when done carefully.

CI gates protect merge and release flow.

Flaky diagnosis protects trust in the engineering system.

For JAX-RS enterprise backend, the goal is not “more tests”.

The goal is:

right tests
right boundary
right signal
right stage
right ownership
Lesson Recap

You just completed lesson 97 in final stretch. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.