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.
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:
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.
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
mvnglobal 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:
- setup yang benar-benar wajib,
- 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_HOMEberbeda, - 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.propertiesmengontrol versi Maven wrapper,.mvn/maven.configbisa menambah argument default,.mvn/jvm.configbisa menambah JVM option Maven,~/.m2/settings.xmlsering menyimpan repository credential,~/.m2/repositoryadalah 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,
.envfile,- 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,
.envtidak 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:
| Mode | Kelebihan | Kekurangan |
|---|---|---|
java -jar | dekat dengan runtime artifact | perlu build dulu |
| Maven exec | cepat untuk dev | bisa beda dari packaged artifact |
| IDE run | nyaman debug | risk config tersembunyi di IDE |
| Docker Compose app | dekat container runtime | feedback lebih lambat |
| Kubernetes local/dev namespace | dekat deployment | setup 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:
| Symptom | Possible Cause | First Checks |
|---|---|---|
java: command not found | JDK belum terpasang/PATH salah | java -version, echo $JAVA_HOME |
| Maven pakai JDK salah | JAVA_HOME berbeda | ./mvnw -version |
| dependency tidak resolve | credential/VPN/repo internal | help:effective-settings, ~/.m2/settings.xml |
| test gagal lokal tapi CI hijau | env/profile/data lokal berbeda | compare command/profile |
| CI gagal tapi lokal hijau | test subset kurang/OS mismatch | run CI-like command |
| app tidak connect DB | DB belum ready/config salah | docker compose ps, psql |
| port already in use | port bentrok | lsof -i :PORT |
| Kafka tidak connect | advertised listener salah | inspect compose/env |
| Redis stale state | old keys | inspect/flush local disposable Redis |
| breakpoint tidak hit | IDE source/classpath mismatch | rebuild/reimport project |
Debugging prinsip:
- cek tool version,
- cek command yang dijalankan,
- cek active profile,
- cek env var,
- cek dependency lokal,
- cek log aplikasi,
- cek perbedaan dengan CI,
- 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
.envlalu 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:
.envmasuk.gitignore,- gunakan
.env.exampletanpa 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.
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.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.