Series MapLesson 37 / 58
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Deepen PracticeOrdered learning track

Local Development Workflow

Repository setup, Java/Maven setup, environment config, local database, message broker, Redis, Docker Compose, seed data, debug mode, IDE integration, test selection, local scripts, onboarding checklist, dan local workflow review untuk Java/JAX-RS enterprise services.

14 min read2721 words
PrevNext
Lesson 3758 lesson track33–48 Deepen Practice
#local-development#developer-productivity#java#jax-rs+8 more

Local Development Workflow

1. Core Idea

Local development workflow adalah cara seorang engineer membawa repository dari keadaan kosong menjadi environment yang bisa:

  • di-build,
  • di-test,
  • dijalankan,
  • di-debug,
  • dihubungkan ke dependency lokal,
  • dibandingkan dengan CI,
  • direproduksi oleh engineer lain.

Untuk senior backend engineer, local workflow bukan sekadar "aplikasi bisa jalan di laptop". Local workflow adalah miniatur production dan CI yang cukup akurat untuk mempercepat feedback tanpa menyembunyikan failure mode penting.

Mental model:

flowchart TD Repo[Repository] --> Toolchain[Java / Maven / Git / Docker] Toolchain --> Config[Runtime Config] Config --> Dependencies[PostgreSQL / Kafka / RabbitMQ / Redis / External Services] Dependencies --> App[Java JAX-RS Service] App --> Test[Unit / Integration / Contract Tests] App --> Debug[IDE / CLI Debugging] Test --> Feedback[Developer Feedback Loop] Debug --> Feedback Feedback --> PR[Pull Request] PR --> CI[CI Pipeline]

Local workflow yang sehat memiliki invariant:

Command lokal yang direkomendasikan harus semirip mungkin dengan command CI, tetapi tetap cukup cepat untuk feedback harian.

Jika local dan CI terlalu berbeda, developer akan sering mengalami:

  • "works on my machine",
  • CI-only failure,
  • dependency missing,
  • profile mismatch,
  • config mismatch,
  • test environment mismatch,
  • build tool mismatch,
  • hidden setup tribal knowledge.

2. Why Local Workflow Matters

Local workflow menentukan kecepatan engineer dalam memahami codebase dan memperbaiki masalah.

Workflow yang buruk menyebabkan:

  • onboarding lambat,
  • banyak waktu habis untuk setup,
  • test jarang dijalankan,
  • CI menjadi tempat pertama menemukan bug,
  • PR lebih lama selesai,
  • debugging dependency lebih sulit,
  • senior engineer menjadi bottleneck karena banyak knowledge tersembunyi,
  • incident fix lambat karena environment reproduksi tidak siap.

Workflow yang baik memungkinkan:

  • build cepat,
  • test terpilih bisa dijalankan cepat,
  • dependency lokal mudah dinyalakan,
  • error setup mudah dipahami,
  • command standar terdokumentasi,
  • local dan CI tidak terlalu jauh,
  • debugging bisa dilakukan tanpa mengubah production-like behavior secara ekstrem.

Untuk Java/JAX-RS service, local workflow biasanya menyentuh:

  • JDK,
  • Maven,
  • Maven wrapper,
  • environment variables,
  • application config,
  • local PostgreSQL,
  • Kafka/RabbitMQ,
  • Redis,
  • Docker Compose,
  • test profile,
  • IDE run configuration,
  • remote debugging,
  • service dependency mocks/stubs,
  • local seed data,
  • database migration.

3. Local Workflow Lifecycle

Local workflow punya lifecycle yang bisa direview seperti production process.

flowchart LR Clone[Clone Repository] --> Tool[Install Toolchain] Tool --> Config[Configure Environment] Config --> Deps[Start Local Dependencies] Deps --> Build[Build] Build --> Test[Test] Test --> Run[Run Service] Run --> Debug[Debug] Debug --> Change[Make Change] Change --> Verify[Verify Locally] Verify --> PR[Open PR]

Setiap stage harus punya command yang jelas, expected result, dan common failure explanation.

Contoh pertanyaan review:

  • Bagaimana engineer baru tahu JDK version yang benar?
  • Apakah harus menggunakan mvn global atau ./mvnw?
  • Bagaimana dependency lokal dinyalakan?
  • Apakah ada seed data?
  • Bagaimana menjalankan unit test saja?
  • Bagaimana menjalankan integration test?
  • Bagaimana menjalankan service dengan profile local?
  • Bagaimana attach debugger?
  • Apa beda command local dengan command CI?
  • Apa yang harus dilakukan jika dependency tidak resolve?
  • Di mana credential private artifact repository dikonfigurasi?

4. Repository Setup

Repository setup minimal harus menjawab:

# clone
# enter directory
# verify tool version
# build
# test
# run

Contoh command pattern:

git clone git@github.com:org/service.git
cd service
./mvnw -version
./mvnw clean verify

Untuk enterprise repository, setup sering lebih kompleks karena ada:

  • private artifact repository,
  • internal parent POM,
  • multi-module build,
  • generated code,
  • internal certificates,
  • Git submodule/subtree,
  • service dependency lokal,
  • Kubernetes config,
  • cloud credentials,
  • message broker setup,
  • database migration.

Senior engineer harus membedakan dua hal:

  1. setup yang benar-benar wajib,
  2. setup yang hanya diperlukan untuk scenario tertentu.

Dokumentasi onboarding yang buruk sering mencampur semua setup menjadi satu instruksi panjang sehingga engineer baru tidak tahu bagian mana yang harus dilakukan sekarang.


5. Java Setup

Java setup harus eksplisit.

Hal yang perlu dikunci:

  • major JDK version,
  • vendor jika relevan,
  • build JDK vs runtime JDK,
  • JAVA_HOME,
  • PATH,
  • compiler release target,
  • IDE language level,
  • container runtime JDK/JRE.

Command verifikasi:

java -version
javac -version
echo "$JAVA_HOME"

Masalah umum:

  • local menggunakan JDK 21, CI menggunakan JDK 17,
  • IDE menggunakan JDK berbeda dari terminal,
  • Maven menggunakan JAVA_HOME berbeda,
  • runtime container menggunakan JRE berbeda dari build JDK,
  • library menggunakan bytecode level lebih tinggi dari runtime,
  • annotation processor behavior berbeda antar JDK.

Untuk Maven project, command ini penting:

./mvnw -version

Output-nya menunjukkan Maven version dan Java runtime yang benar-benar dipakai Maven.


6. Maven Setup

Local workflow Maven yang sehat sebaiknya menggunakan Maven wrapper:

./mvnw clean verify

Bukan:

mvn clean verify

Kecuali tim memang sengaja mengizinkan Maven global.

Hal yang perlu dipahami:

  • .mvn/wrapper/maven-wrapper.properties mengontrol versi Maven wrapper,
  • .mvn/maven.config bisa menambah argument default,
  • .mvn/jvm.config bisa menambah JVM option Maven,
  • ~/.m2/settings.xml sering menyimpan repository credential,
  • ~/.m2/repository adalah local artifact cache,
  • private repository mungkin butuh VPN, token, atau certificate.

Command yang berguna:

./mvnw -version
./mvnw help:effective-pom
./mvnw help:effective-settings
./mvnw dependency:tree

Failure mode:

  • dependency tidak resolve karena credential hilang,
  • parent POM tidak resolve,
  • snapshot dependency stale,
  • local cache corrupted,
  • proxy/VPN/internal repository unreachable,
  • Maven global berbeda dari Maven wrapper,
  • test profile berbeda dari CI.

7. Environment Configuration

Runtime configuration lokal harus terlihat dan dapat diaudit.

Sumber config umum:

  • environment variable,
  • .env file,
  • application properties/YAML,
  • Maven profile,
  • JVM system property,
  • IDE run configuration,
  • Docker Compose environment,
  • Kubernetes ConfigMap/Secret untuk environment yang lebih production-like.

Contoh command:

printenv | sort
printenv | grep -E 'JAVA|MAVEN|APP|DB|KAFKA|REDIS'

Untuk Java service:

java -Dconfig.profile=local -jar target/service.jar

atau melalui Maven:

./mvnw -DskipTests package
java -jar target/*.jar

Masalah umum:

  • env var typo,
  • config key berubah tapi local guide tidak diupdate,
  • IDE run config punya env var yang tidak ada di terminal,
  • .env tidak disinkronkan dengan Docker Compose,
  • secret local tersebar di shell history,
  • local profile menonaktifkan behavior penting yang ada di CI/production.

Rule praktis:

Local config boleh menyederhanakan dependency, tetapi tidak boleh menyembunyikan behavior correctness yang penting.


8. Local Database Workflow

Untuk service Quote/Order atau CPQ-style system, database lokal sering penting karena domain behavior kuat bergantung pada persistence.

Local database workflow harus menjawab:

  • database engine apa,
  • version berapa,
  • schema dibuat oleh apa,
  • migration tool apa,
  • seed data dari mana,
  • user/password local apa,
  • bagaimana reset data,
  • bagaimana inspect data,
  • bagaimana menjalankan integration test.

Contoh Docker Compose service:

services:
  postgres:
    image: postgres:16
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app

Command verifikasi:

docker compose up -d postgres
docker compose ps
psql "postgresql://app:app@localhost:5432/app" -c 'select 1'

Failure mode:

  • port 5432 bentrok,
  • database belum ready saat app start,
  • migration belum jalan,
  • schema lokal berbeda dari CI,
  • seed data outdated,
  • timezone/collation berbeda,
  • extension PostgreSQL tidak tersedia,
  • test mengasumsikan state dari test sebelumnya.

Senior review concern:

  • apakah reset database aman,
  • apakah migration idempotent,
  • apakah seed data representatif,
  • apakah local database terlalu jauh dari production behavior,
  • apakah integration test menggunakan database disposable.

9. Local Message Broker Workflow

Untuk microservices yang memakai Kafka atau RabbitMQ, local broker workflow harus jelas.

Pertanyaan utama:

  • Kafka/RabbitMQ benar-benar wajib untuk menjalankan service?
  • Apakah ada stub/mock mode?
  • Topic/queue/exchange dibuat otomatis atau manual?
  • Schema/contract event disimpan di mana?
  • Consumer group local seperti apa?
  • Bagaimana reset topic/queue local?
  • Bagaimana inspect message?

Contoh command pattern:

docker compose up -d kafka rabbitmq

Kafka failure mode:

  • advertised listener salah,
  • topic belum dibuat,
  • consumer group offset membuat event tidak terbaca,
  • schema registry tidak tersedia,
  • local broker berbeda versi dari environment test,
  • event compatibility tidak diuji.

RabbitMQ failure mode:

  • exchange belum dibuat,
  • binding salah,
  • queue durable/auto-delete mismatch,
  • dead-letter configuration hilang,
  • credential salah,
  • management UI port bentrok.

Senior engineer harus menghindari local setup yang membuat messaging behavior terlalu palsu sehingga bug ordering, retry, idempotency, dead-letter, atau schema compatibility tidak terlihat.


10. Local Redis Workflow

Redis lokal sering digunakan untuk:

  • cache,
  • distributed lock,
  • rate limit,
  • session-like state,
  • temporary coordination,
  • idempotency key,
  • feature cache.

Local workflow harus menjawab:

  • Redis wajib atau optional,
  • TTL behavior diuji atau tidak,
  • key prefix lokal apa,
  • cara flush data lokal,
  • apakah cluster/sentinel behavior relevan,
  • apakah local Redis single-node cukup.

Command dasar:

docker compose up -d redis
redis-cli -h localhost -p 6379 ping
redis-cli keys '*'

Hati-hati dengan:

redis-cli FLUSHALL

Command tersebut destructive. Aman untuk local disposable Redis, berbahaya jika context salah.

Failure mode:

  • stale cache membuat behavior membingungkan,
  • TTL tidak sesuai,
  • serialization format berubah,
  • key collision antar developer/test,
  • local Redis tidak merepresentasikan cluster behavior,
  • feature flag/cache invalidation tidak diuji.

11. Docker Compose as Local Dependency Orchestrator

Docker Compose cocok untuk dependency lokal seperti:

  • PostgreSQL,
  • Kafka,
  • RabbitMQ,
  • Redis,
  • mock server,
  • schema registry,
  • object storage emulator,
  • local observability tools.

Namun Compose harus dipakai dengan disiplin.

Checklist Compose sehat:

  • service name jelas,
  • port mapping terdokumentasi,
  • volume disposable vs persistent jelas,
  • healthcheck ada untuk dependency penting,
  • env var tidak mengandung secret real,
  • reset command jelas,
  • version image dipin,
  • tidak memakai latest,
  • tidak memaksa engineer menjalankan dependency yang tidak diperlukan.

Contoh healthcheck:

healthcheck:
  test: ["CMD-SHELL", "pg_isready -U app -d app"]
  interval: 5s
  timeout: 3s
  retries: 20

Failure mode:

  • dependency belum ready tapi app sudah start,
  • port bentrok dengan service lokal lain,
  • volume lama menyimpan schema/data usang,
  • image berubah karena tag floating,
  • Compose behavior berbeda di macOS/Windows/Linux,
  • network name berubah sehingga connection string salah.

12. Seed Data and Test Fixtures

Seed data lokal harus diperlakukan sebagai tooling artifact.

Seed data buruk menyebabkan:

  • demo lokal tidak stabil,
  • integration test misleading,
  • domain state tidak realistis,
  • engineer mengubah data manual tanpa tracking,
  • bug hanya muncul di environment shared.

Seed data yang baik:

  • minimal tetapi representatif,
  • versioned,
  • bisa di-reset,
  • tidak mengandung data sensitive,
  • mencakup happy path dan edge case,
  • selaras dengan migration,
  • documented.

Untuk Quote/Order domain, seed data bisa mencakup:

  • product/catalog sample,
  • customer/account sample,
  • quote lifecycle states,
  • order states,
  • pricing scenario,
  • invalid/edge state untuk debugging.

Tetapi detail domain internal harus diverifikasi di codebase dan dengan tim.


13. Running the Service Locally

Command run lokal harus eksplisit.

Possible patterns:

./mvnw clean package
java -jar target/service.jar
./mvnw compile exec:java
./scripts/run-local.sh
docker compose up app

Senior engineer harus memahami trade-off:

ModeKelebihanKekurangan
java -jardekat dengan runtime artifactperlu build dulu
Maven execcepat untuk devbisa beda dari packaged artifact
IDE runnyaman debugrisk config tersembunyi di IDE
Docker Compose appdekat container runtimefeedback lebih lambat
Kubernetes local/dev namespacedekat deploymentsetup lebih berat dan risk permission

Rule praktis:

Sediakan minimal dua mode: fast local loop dan production-like local loop.

Fast local loop untuk development harian. Production-like loop untuk debugging issue yang terkait packaging, container, env, atau dependency.


14. Debug Mode and IDE Integration

Local debug workflow harus tidak misterius.

Java remote debug pattern:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005

Contoh run:

JAVA_TOOL_OPTIONS='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005' \
  java -jar target/service.jar

Hal yang perlu diperjelas:

  • debug port,
  • suspend mode,
  • profile yang digunakan,
  • env var yang dibutuhkan,
  • working directory,
  • classpath/module setup,
  • generated sources,
  • annotation processing,
  • test debug configuration.

Failure mode:

  • IDE run config tidak sama dengan CLI,
  • breakpoint tidak hit karena source mismatch,
  • generated source tidak dikenali IDE,
  • annotation processor belum aktif,
  • debug port bentrok,
  • service timeout karena breakpoint terlalu lama,
  • local debug menonaktifkan async behavior yang penting.

15. Test Selection Workflow

Senior engineer tidak selalu menjalankan semua test setiap perubahan kecil. Namun pemilihan test harus sadar risiko.

Test selection pattern:

# all unit tests
./mvnw test

# full verification
./mvnw verify

# one test class
./mvnw -Dtest=QuoteServiceTest test

# one test method
./mvnw -Dtest=QuoteServiceTest#shouldCalculateQuoteTotal test

# integration tests via failsafe
./mvnw verify -DskipITs=false

Trade-off:

  • menjalankan test sedikit: cepat tetapi risk miss regression,
  • menjalankan full suite: aman tetapi lambat,
  • menjalankan affected module: optimal tetapi butuh dependency graph benar,
  • mengandalkan CI penuh: local cepat tetapi feedback terlambat.

Review concern:

  • apakah README menjelaskan test subset,
  • apakah integration test dipisah jelas,
  • apakah flaky test diketahui,
  • apakah Testcontainers butuh Docker,
  • apakah test parallel aman,
  • apakah local test memakai real dependency atau mock.

16. Local Scripts

Script lokal harus menjadi interface stabil untuk developer.

Contoh command yang idealnya tersedia:

./scripts/bootstrap.sh
./scripts/test.sh
./scripts/run-local.sh
./scripts/reset-local-db.sh
./scripts/format.sh
./scripts/lint.sh
./scripts/debug-env.sh

Script yang baik:

  • punya --help,
  • memvalidasi prerequisite,
  • memberikan error message jelas,
  • idempotent,
  • aman untuk rerun,
  • tidak menyembunyikan command penting,
  • bisa dipakai CI jika relevan,
  • tidak mencetak secret,
  • memiliki dry-run untuk destructive action.

Anti-pattern:

rm -rf "$DIR"

tanpa validasi bahwa $DIR tidak kosong dan berada di path yang aman.

Lebih aman:

if [[ -z "${DIR:-}" || "$DIR" == "/" ]]; then
  echo "Refusing to delete unsafe DIR: ${DIR:-<empty>}" >&2
  exit 1
fi
rm -rf -- "$DIR"

17. Local vs CI Parity

Local dan CI tidak harus identik, tetapi perbedaannya harus diketahui.

Area parity yang penting:

  • JDK version,
  • Maven version,
  • Maven command,
  • active profile,
  • dependency version,
  • test separation,
  • database migration,
  • environment variable,
  • Docker image build,
  • static analysis,
  • formatting/linting,
  • security scanning.

Contoh mismatch:

# local
./mvnw test

# CI
./mvnw clean verify -Pci -DskipITs=false

Ini tidak salah, tetapi harus documented. Developer harus tahu kapan perlu menjalankan command CI-like sebelum PR.

Good practice:

./scripts/ci-local.sh

Command ini menjalankan subset paling dekat dengan CI yang realistis untuk laptop.


18. Local Workflow Failure Modes

Failure mode umum:

SymptomPossible CauseFirst Checks
java: command not foundJDK belum terpasang/PATH salahjava -version, echo $JAVA_HOME
Maven pakai JDK salahJAVA_HOME berbeda./mvnw -version
dependency tidak resolvecredential/VPN/repo internalhelp:effective-settings, ~/.m2/settings.xml
test gagal lokal tapi CI hijauenv/profile/data lokal berbedacompare command/profile
CI gagal tapi lokal hijautest subset kurang/OS mismatchrun CI-like command
app tidak connect DBDB belum ready/config salahdocker compose ps, psql
port already in useport bentroklsof -i :PORT
Kafka tidak connectadvertised listener salahinspect compose/env
Redis stale stateold keysinspect/flush local disposable Redis
breakpoint tidak hitIDE source/classpath mismatchrebuild/reimport project

Debugging prinsip:

  1. cek tool version,
  2. cek command yang dijalankan,
  3. cek active profile,
  4. cek env var,
  5. cek dependency lokal,
  6. cek log aplikasi,
  7. cek perbedaan dengan CI,
  8. dokumentasikan fix jika recurring.

19. Production and Architecture Concerns

Local workflow punya efek ke architecture dan production quality.

Jika local workflow terlalu palsu:

  • event compatibility tidak terlihat,
  • DB migration risk tidak terlihat,
  • cache invalidation bug tidak terlihat,
  • config precedence bug tidak terlihat,
  • packaging/container bug baru muncul di CI/deploy,
  • integration issue muncul terlambat.

Jika local workflow terlalu berat:

  • engineer jarang menjalankan test,
  • feedback loop lambat,
  • orang bypass script,
  • CI menjadi satu-satunya validator,
  • onboarding melelahkan.

Balance yang baik:

  • fast local unit loop,
  • reliable integration loop,
  • documented production-like loop,
  • CI sebagai final gate,
  • runbook untuk issue sulit.

20. Security Concerns

Local workflow sering menjadi sumber leakage.

Risiko:

  • secret disimpan di .env lalu ter-commit,
  • token terlihat di shell history,
  • credential private repo tersebar di README,
  • production credential dipakai lokal,
  • local script mencetak env lengkap,
  • Docker volume menyimpan data sensitive,
  • copied production data tidak diredact,
  • debug logs mencetak request body sensitive.

Checklist keamanan:

  • .env masuk .gitignore,
  • gunakan .env.example tanpa secret real,
  • jangan menaruh token di command inline,
  • gunakan credential helper/secret manager sesuai policy,
  • jangan commit settings.xml,
  • jangan gunakan production secret untuk local,
  • redact evidence sebelum dibagikan,
  • pastikan script tidak print secret.

21. PR Review Checklist

Saat mereview perubahan local workflow, tanyakan:

  • Apakah command baru documented?
  • Apakah command bisa dijalankan ulang dengan aman?
  • Apakah destructive action punya guard?
  • Apakah secret tidak tercetak?
  • Apakah local dan CI tetap selaras?
  • Apakah JDK/Maven version eksplisit?
  • Apakah Docker image version dipin?
  • Apakah seed data versioned dan aman?
  • Apakah setup cross-platform cukup?
  • Apakah error message membantu engineer baru?
  • Apakah script punya --help?
  • Apakah perubahan mempercepat feedback tanpa menyembunyikan correctness issue?

22. Internal Verification Checklist

Untuk konteks CSG/team, jangan mengasumsikan detail internal. Verifikasi:

  • repository setup resmi,
  • required JDK version,
  • required Maven version,
  • apakah wajib menggunakan Maven wrapper,
  • private artifact repository dan credential setup,
  • local PostgreSQL setup,
  • local Kafka/RabbitMQ setup,
  • local Redis setup,
  • Docker Compose file resmi,
  • seed data resmi,
  • migration tool,
  • profile local/CI/test,
  • script bootstrap/test/run/debug,
  • IDE setup yang direkomendasikan,
  • local vs CI command difference,
  • common onboarding failure,
  • runbook untuk local setup issue,
  • security rule untuk secret lokal,
  • apakah boleh menggunakan production-like data lokal,
  • siapa owner tooling/local setup.

23. Senior Engineer Operating Habit

Senior engineer tidak hanya memakai local workflow. Senior engineer memperbaiki workflow ketika friction berulang.

Kebiasaan yang bagus:

  • catat setup failure pertama kali muncul,
  • ubah tribal knowledge menjadi script atau docs,
  • sederhanakan command yang sering dipakai,
  • buat error message lebih jelas,
  • samakan local dan CI sejauh mungkin,
  • pisahkan fast path dan full verification path,
  • jangan mengandalkan IDE-only configuration,
  • jaga local workflow tetap secure,
  • review perubahan tooling dengan standar production impact.

Local workflow yang baik adalah leverage. Ia mempercepat semua engineer setiap hari.

Lesson Recap

You just completed lesson 37 in deepen practice. 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.