Series MapLesson 03 / 35
Start HereOrdered learning track

Learn Java Error Reliability Observability Part 003 Error Taxonomy

20 min read3925 words
PrevNext
Lesson 0335 lesson track0106 Start Here

title: Learn Java Error, Reliability & Observability Engineering - Part 003 description: Taxonomy error produksi untuk aplikasi Java: membedakan bug, domain failure, dependency failure, overload, timeout, data inconsistency, dan policy failure secara operasional. series: learn-java-error-reliability-observability seriesTitle: Learn Java Error, Reliability & Observability Engineering order: 3 partTitle: Error Taxonomy tags:

  • java
  • error-handling
  • reliability
  • failure-taxonomy
  • observability
  • production-engineering date: 2026-06-28

Part 003 — Error Taxonomy

Target part ini: kita tidak lagi menyebut semua kegagalan sebagai “error”. Kita akan membangun taxonomy yang cukup presisi untuk menentukan aksi: reject, retry, rollback, compensate, quarantine, degrade, alert, escalate, atau ignore.

Di produksi, kata “error” terlalu miskin. Ia bisa berarti bug, input invalid, database down, timeout, overload, data corrupt, race condition, permission denied, kontrak API berubah, atau proses shutdown sedang berjalan. Jika semua kasus itu diperlakukan sama, sistem akan punya tiga penyakit klasik:

  1. wrong recovery — melakukan retry pada error yang tidak aman untuk di-retry;
  2. wrong visibility — menyembunyikan error yang seharusnya terlihat, atau membocorkan detail internal ke client;
  3. wrong ownership — melempar masalah ke tim/layer yang salah.

Taxonomy error adalah fondasi untuk seluruh seri ini. Exception hierarchy, error code, logging, metrics, tracing, alerting, dan graceful shutdown akan buruk jika taxonomy dasarnya kabur.


1. Kaufman Deconstruction: Skill yang Ingin Dilatih

Dalam kerangka The First 20 Hours, kita memecah kemampuan “menangani error” menjadi kemampuan yang bisa dilatih:

Sub-skillPertanyaan LatihanOutput yang Diinginkan
ClassifyError ini berasal dari mana?Source of failure jelas.
Assess impactSiapa/apa yang terdampak?Blast radius diketahui.
Assess recoverabilityApakah sistem bisa pulih otomatis?Retry/degrade/escalate tidak asal.
Choose boundary behaviorApa yang boleh keluar dari layer ini?Error contract stabil.
Emit evidenceBukti apa yang perlu direkam?Log/metric/trace berguna.
Assign ownershipTim/layer mana yang harus memperbaiki?Incident tidak ping-pong.

Kita akan memakai satu aturan dasar:

Error category yang baik harus membantu keputusan operasional. Jika kategori tidak mengubah aksi, mungkin kategori itu hanya kosmetik.

Contoh kategori yang buruk:

SystemError
ApplicationError
GeneralException
TechnicalException
BusinessException

Kategori tersebut terdengar formal, tetapi sering tidak membantu menjawab:

  • boleh retry atau tidak?
  • client salah atau server salah?
  • data aman atau perlu quarantine?
  • perlu alert sekarang atau cukup dicatat?
  • harus rollback, compensate, atau continue?

2. Fault, Error, Failure: Tiga Level yang Berbeda

Sebelum taxonomy, bedakan tiga konsep berikut.

KonsepMaknaContoh
FaultCacat laten atau kondisi penyebab.Bug mapping enum, index DB hilang, credential salah, CPU limit terlalu kecil.
ErrorState internal sudah menyimpang dari yang benar.Object invalid masuk service, connection pool exhausted, deadline habis.
FailurePerilaku eksternal tidak memenuhi kontrak.Client menerima 500, job tidak diproses, event terkirim dua kali.

Diagramnya:

Contoh dalam Java service:

Fault:
  Developer lupa handle status CLOSED dalam state machine enforcement case.

Error:
  Service menghasilkan transition decision null.

Failure:
  API /cases/{id}/actions mengembalikan 500 saat officer membuka case CLOSED.

Engineer top-tier tidak berhenti di failure. Ia bertanya balik:

Failure apa yang terlihat?
Error internal apa yang mendahului?
Fault apa yang memungkinkan error itu terjadi?
Kontrol apa yang seharusnya memutus rantai tersebut?

3. Taxonomy Besar: 10 Dimensi Error Produksi

Tidak ada satu taxonomy tunggal yang cukup untuk semua keputusan. Error produksi perlu dilihat dari beberapa dimensi.

Kita akan uraikan setiap dimensi.


4. Dimensi 1 — Origin: Dari Mana Error Berasal?

Origin membantu menentukan ownership dan boundary translation.

4.1 Programmer Error

Definisi: error karena kode melanggar asumsi internalnya sendiri.

Contoh:

  • NullPointerException karena invariant object tidak dijaga;
  • IllegalStateException karena state machine masuk state mustahil;
  • IndexOutOfBoundsException karena bug akses collection;
  • ClassCastException karena asumsi type salah;
  • AssertionError atau failed precondition internal;
  • branch default yang seharusnya unreachable ternyata tercapai.

Contoh kode:

Action nextAction(CaseStatus status) {
    return switch (status) {
        case OPEN -> Action.REVIEW;
        case UNDER_INVESTIGATION -> Action.REQUEST_EVIDENCE;
        case CLOSED -> throw new IllegalStateException(
            "Closed case must not request next officer action"
        );
    };
}

Karakteristik:

AspekNilai Umum
Retry?Tidak berguna, kecuali fault intermittent dari state external.
Client fault?Tidak selalu. Biasanya server defect.
Alert?Ya, jika mencapai boundary produksi.
Response500 atau internal failure contract.
EvidenceStack trace, correlation ID, input shape, state snapshot aman.

Anti-pattern:

try {
    return nextAction(status);
} catch (Exception e) {
    return Action.REVIEW; // hides a broken invariant
}

Ini bukan graceful degradation. Ini mengubah bug menjadi keputusan bisnis palsu.


4.2 Domain Error

Definisi: permintaan valid secara teknis, tetapi melanggar aturan domain.

Contoh regulatory/case-management:

  • case sudah CLOSED, tidak boleh ditambah evidence baru;
  • officer tidak boleh approve case yang ia buat sendiri;
  • penalty tidak boleh diterbitkan sebelum notice period selesai;
  • appeal window sudah lewat;
  • escalation membutuhkan minimal dua failed reminders.

Domain error bukan “exceptional” dalam arti operasional. Ia adalah outcome bisnis yang harus bisa dijelaskan.

public final class CaseAlreadyClosedException extends RuntimeException {
    private final String caseId;
    private final CaseStatus status;

    public CaseAlreadyClosedException(String caseId, CaseStatus status) {
        super("Case %s is already %s".formatted(caseId, status));
        this.caseId = caseId;
        this.status = status;
    }

    public String caseId() {
        return caseId;
    }

    public CaseStatus status() {
        return status;
    }
}

Karakteristik:

AspekNilai Umum
Retry?Tidak, kecuali state domain berubah.
Client fault?Sering ya, tetapi bukan selalu “bad request”.
Alert?Biasanya tidak, kecuali rate melonjak.
Response400/403/409/422 tergantung kontrak.
EvidenceRule ID, case ID, actor ID, decision reason.

Mental model:

Domain error adalah bagian dari bahasa domain. Ia harus dapat dibaca oleh manusia bisnis, diuji oleh QA, dipakai oleh audit, dan dipetakan ke response contract.


4.3 Input/Contract Error

Definisi: request/event/file/command tidak memenuhi kontrak teknis.

Contoh:

  • field wajib kosong;
  • enum tidak dikenal;
  • timestamp format salah;
  • payload melebihi batas ukuran;
  • schema version tidak didukung;
  • message event hilang correlationId;
  • parameter query bertentangan.

Input error berbeda dari domain error.

Input ErrorDomain Error
Payload/command tidak valid secara kontrak.Payload valid, tetapi aksi tidak diperbolehkan oleh aturan bisnis.
Biasanya terjadi sebelum domain service.Biasanya terjadi di domain/application service.
Error detail bisa menunjuk field/path.Error detail menunjuk rule/process/state.

Contoh shape validasi:

public record ValidationIssue(
    String field,
    String code,
    String message
) {}

public final class RequestValidationException extends RuntimeException {
    private final List<ValidationIssue> issues;

    public RequestValidationException(List<ValidationIssue> issues) {
        super("Request validation failed with %d issue(s)".formatted(issues.size()));
        this.issues = List.copyOf(issues);
    }

    public List<ValidationIssue> issues() {
        return issues;
    }
}

Karakteristik:

AspekNilai Umum
Retry?Tidak dengan payload yang sama.
Client fault?Ya, jika client mengirim request.
Alert?Tidak per request; alert jika spike/anomaly.
Response400 atau 422 sesuai kontrak.
EvidenceField path, error code, schema version.

4.4 Dependency Error

Definisi: service bergantung pada komponen lain dan komponen tersebut gagal, lambat, berubah kontrak, atau memberi hasil tidak valid.

Contoh:

  • payment service timeout;
  • identity service mengembalikan 503;
  • database deadlock;
  • Kafka broker unavailable;
  • S3-compatible storage slow;
  • external API mengubah enum tanpa pemberitahuan;
  • downstream mengembalikan 200 dengan body invalid.

Dependency error perlu dibaca dari dua sisi:

Dari sisi kita:
  dependency failed.

Dari sisi user kita:
  service kita tetap bertanggung jawab terhadap contract kita sendiri.

Karakteristik:

AspekNilai Umum
Retry?Mungkin, jika operasi idempotent dan timeout budget masih cukup.
Client fault?Tidak.
Alert?Ya jika memengaruhi SLO atau dependency critical.
Response502/503/504 atau degraded response.
EvidenceDependency name, operation, status, latency, attempt count, timeout budget.

Contoh wrapper:

public final class DependencyFailureException extends RuntimeException {
    private final String dependency;
    private final String operation;
    private final boolean retryable;

    public DependencyFailureException(
        String dependency,
        String operation,
        boolean retryable,
        Throwable cause
    ) {
        super("Dependency failure: %s.%s retryable=%s"
            .formatted(dependency, operation, retryable), cause);
        this.dependency = dependency;
        this.operation = operation;
        this.retryable = retryable;
    }

    public boolean retryable() {
        return retryable;
    }
}

Anti-pattern:

catch (Exception e) {
    throw new RuntimeException("Failed");
}

Informasi dependency, operation, status, dan retryability hilang. Observability menjadi miskin.


4.5 Infrastructure/Platform Error

Definisi: runtime environment atau platform execution tidak mampu menyediakan resource/condition yang dibutuhkan.

Contoh:

  • container mendapat SIGTERM;
  • disk penuh;
  • DNS resolution gagal;
  • node kehilangan network;
  • CPU throttling ekstrem;
  • file descriptor habis;
  • certificate expired;
  • config/secrets tidak tersedia;
  • clock skew;
  • pod evicted.

Karakteristik:

AspekNilai Umum
Retry?Kadang, tetapi retry lokal bisa memperburuk overload.
Client fault?Tidak.
Alert?Ya, biasanya platform/SRE/app owner.
Response503/504, fail startup, readiness false, graceful drain.
EvidenceHost/pod, resource usage, config version, env, lifecycle event.

Infrastructure error sering tampak sebagai dependency error. Misalnya database timeout bisa berasal dari DB lambat, network drop, connection pool penuh, DNS issue, atau CPU throttling aplikasi sendiri. Karena itu taxonomy harus dikombinasikan dengan telemetry.


4.6 Runtime/JVM Error

Definisi: JVM atau runtime Java mengalami kondisi serius.

Contoh:

  • OutOfMemoryError;
  • StackOverflowError;
  • NoClassDefFoundError;
  • ExceptionInInitializerError;
  • classpath/module mismatch;
  • native memory exhaustion;
  • fatal JVM crash;
  • GC overhead ekstrem.

Di Java, banyak kondisi ini muncul sebagai subclass Error, bukan Exception. Secara umum aplikasi normal tidak boleh menganggap Error sebagai kondisi bisnis yang recoverable.

Karakteristik:

AspekNilai Umum
Retry?Tidak di request-level. Restart/rollout mungkin.
Client fault?Tidak.
Alert?Ya, high severity.
ResponseFail fast, crash, restart, reject traffic.
EvidenceHeap dump, thread dump, GC log, classpath, startup log.

Prinsip:

Jangan membangun flow bisnis yang bergantung pada menangkap Error. Tangani di boundary paling luar hanya untuk evidence dan shutdown bila masih mungkin.


4.7 Policy/Security Error

Definisi: aksi ditolak karena policy, izin, risk control, atau security constraint.

Contoh:

  • user tidak punya permission;
  • token expired;
  • request melanggar rate limit;
  • tenant mismatch;
  • akses data lintas wilayah dilarang;
  • action butuh approval tambahan;
  • data classification melarang export.

Policy error sering mirip domain error, tetapi bedanya:

Policy ErrorDomain Error
Berbasis actor, permission, risk, compliance, security, tenancy.Berbasis state dan rule proses bisnis.
Sering harus menghindari detail leakage.Sering harus memberi reason yang audit-friendly.
Bisa menjadi security signal.Biasanya business-process signal.

Karakteristik:

AspekNilai Umum
Retry?Tidak kecuali credential/permission berubah.
Client fault?Ya/tidak tergantung konteks.
Alert?Tidak selalu; alert jika anomaly/attack pattern.
Response401/403/429, atau opaque denial.
EvidenceActor, tenant, policy ID, decision, risk score; jangan log secret.

4.8 Data Integrity Error

Definisi: data melanggar invariant, constraint, consistency expectation, atau tidak bisa dipercaya.

Contoh:

  • duplicate natural key;
  • foreign key missing;
  • state machine transition tidak konsisten;
  • event order terbalik;
  • read model stale di luar toleransi;
  • checksum mismatch;
  • record punya status APPROVED tetapi approval timestamp null;
  • optimistic lock conflict;
  • partial migration.

Data integrity error perlu diperlakukan lebih hati-hati daripada error biasa. Salah recovery bisa membuat data makin rusak.

Karakteristik:

AspekNilai Umum
Retry?Tergantung: optimistic conflict mungkin retry; corrupt data tidak.
Client fault?Kadang.
Alert?Ya jika invariant internal/data critical rusak.
Response409, 500, quarantine, manual review.
EvidenceEntity ID, invariant ID, version, transaction ID, source event.

Prinsip:

Untuk data integrity error, default yang aman adalah stop, isolate, preserve evidence. Jangan “fix silently” kecuali mekanisme repair sudah dirancang dan diaudit.


5. Dimensi 2 — Recoverability: Apa Aksi yang Benar?

Origin menjelaskan sumber. Recoverability menjelaskan tindakan.

CategoryMaknaAksi Umum
RetryableKemungkinan berhasil jika dicoba lagi.Retry dengan budget, jitter, idempotency.
Non-retryablePayload/state sama akan gagal lagi.Reject, return error, fix input/state.
CompensatableEfek sudah terjadi sebagian, perlu aksi balik.Compensation, saga rollback, manual review.
DegradableFitur bisa turun kualitas tanpa gagal total.Fallback, stale read, partial response.
EscalatableButuh manusia atau sistem lain.Alert, ticket, runbook.
QuarantinableItem buruk bisa diisolasi agar pipeline lanjut.Dead-letter, quarantine table, hold queue.
FatalProses tidak aman untuk lanjut.Crash/restart, readiness false, fail startup.

5.1 Retryability Bukan Properti Exception Saja

Retryability bergantung pada operasi.

Contoh:

Timeout saat GET /profile:
  Mungkin retryable.

Timeout setelah POST /payments tanpa idempotency key:
  Outcome unknown. Retry bisa membuat double charge.

Deadlock pada transaction idempotent:
  Mungkin retryable.

Validation error missing field:
  Non-retryable dengan payload yang sama.

Karena itu jangan membuat class seperti ini tanpa konteks:

class RetryableException extends RuntimeException {}

Lebih baik metadata retryability melekat pada failure decision:

public record FailureDecision(
    FailureKind kind,
    boolean retryable,
    boolean idempotencyRequired,
    boolean alertable,
    String publicCode
) {}

6. Dimensi 3 — Scope dan Blast Radius

Error yang sama bisa berbeda severity tergantung scope.

ScopeContohImplikasi
LocalSatu method gagal parse value.Bisa ditangani lokal.
RequestSatu HTTP request gagal.Return error response.
Workflow itemSatu case/event/job gagal.Quarantine atau manual review.
TenantSemua request tenant A gagal.Tenant-specific incident.
InstanceSatu pod/service instance gagal.Remove dari load balancer.
DependencySemua call ke payment service gagal.Circuit breaker/degradation.
RegionRegion tertentu bermasalah.Failover/traffic shift.
GlobalSemua sistem terdampak.Major incident.

Diagram propagasi:

Tujuan reliability control adalah memotong propagasi sedini mungkin.


7. Dimensi 4 — Visibility dan Audience

Tidak semua informasi error cocok untuk semua audience.

AudienceButuh ApaTidak Boleh Mendapat Apa
End userPesan aman, actionability, reference ID.Stack trace, SQL, secret, internal topology.
Client systemStable code, retry hint, field issue.Detail internal non-kontraktual.
OperatorSeverity, dependency, latency, affected scope.PII/secret tidak perlu.
DeveloperStack trace, cause chain, input shape aman.Data sensitif mentah.
AuditorRule ID, actor, decision, timestamp, evidence.Noise teknis yang tidak relevan.
Security teamActor, tenant, policy, anomaly pattern.Token/password/API key.

Satu failure biasanya perlu multi-representation:

Kesalahan umum: memakai message exception sebagai response publik.

catch (Exception e) {
    return Response.serverError()
        .entity(Map.of("error", e.getMessage()))
        .build();
}

Masalah:

  • message bisa mengandung detail internal;
  • message tidak stabil sebagai contract;
  • message sulit dipakai mesin;
  • localization/audit menjadi kacau;
  • perubahan wording bisa memecahkan client test.

8. Dimensi 5 — Temporal Behavior

Error juga harus dilihat dari pola waktunya.

Temporal TypeMaknaContohRespons
ImmediateGagal langsung.Validation error.Reject cepat.
DelayedEfek gagal muncul belakangan.Async job gagal setelah API 202.Tracking, callback, audit.
IntermittentKadang gagal kadang berhasil.Network flake, race, timeout.Retry terbatas, telemetry.
PersistentSelalu gagal sampai ada fix.Bad config, schema mismatch.Stop retry storm, alert.
ProgressiveMemburuk perlahan.Memory leak, queue backlog.Early warning metric.
BurstSpike mendadak.Traffic surge, dependency outage.Load shedding, autoscale, circuit.

Java service sering salah memperlakukan persistent error sebagai intermittent error. Contoh: config credential salah tetapi kode retry terus setiap request. Hasilnya log spam, dependency pressure, dan alert fatigue.


9. Dimensi 6 — Consistency Outcome

Untuk operasi mutasi, pertanyaan penting bukan hanya “gagal atau berhasil”, tetapi “efeknya sudah terjadi atau belum?”.

OutcomeMaknaContoh
No mutationBelum ada perubahan.Validation gagal sebelum transaction.
Rolled backPerubahan dibatalkan.Exception dalam transaction DB rollback.
CommittedPerubahan berhasil commit.DB update sukses, response hilang.
Partial commitSebagian efek terjadi.DB commit berhasil, event publish gagal.
Duplicate effectEfek terjadi lebih dari sekali.Retry create payment tanpa idempotency.
Unknown outcomeSistem tidak tahu efek terjadi atau tidak.Timeout setelah request downstream dikirim.

Unknown outcome adalah salah satu kategori paling berbahaya.

Client timeout != operation did not happen.
Database timeout != transaction did not commit.
HTTP timeout after sending body != downstream did not process.

Mental model:

Setelah mutasi melewati boundary eksternal, kegagalan komunikasi sering membuat outcome tidak pasti. Recovery harus berbasis idempotency, reconciliation, atau query-by-key, bukan asumsi.


10. Dimensi 7 — Determinism

TypeMaknaDebugging Style
DeterministicInput/state sama selalu gagal.Reproduce locally, unit/integration test.
NondeterministicBergantung timing, interleaving, load.Stress test, tracing, thread dump, metrics.
Environment-dependentHanya gagal di env tertentu.Config diff, dependency version, resource limits.
Data-dependentHanya gagal untuk subset data.Data profiling, invariant query, quarantine.

Contoh:

NullPointerException untuk semua request create-case:
  deterministic programmer error.

Timeout hanya saat traffic puncak:
  load-dependent reliability error.

Error hanya tenant tertentu:
  data/tenant/config-dependent.

Taxonomy yang baik mempercepat pemilihan alat debugging.


11. Dimensi 8 — Ownership

Ownership bukan untuk menyalahkan orang. Ownership untuk mempercepat perbaikan.

OwnerError yang Biasanya Dimiliki
Client/app integratorpayload invalid, unsupported schema, auth token salah.
Domain/application teamrule bug, exception hierarchy salah, transaction boundary salah.
Platform/SREnode, DNS, resource limit, deployment, readiness, autoscaling.
Dependency ownerdownstream outage, contract break, latency regression.
Data/platform teamdata corruption, migration failure, replication lag.
Security/compliancepolicy violation, suspicious access, secret leakage.

Error event harus mengandung cukup metadata untuk ownership:

service.name
service.version
environment
operation
error.kind
error.code
dependency.name
tenant.id
correlation.id
trace.id

12. Error Classification Matrix

Satu taxonomy praktis untuk service Java produksi:

CategoryRetryAlertPublic CodeTypical HTTPNotes
INPUT_INVALIDNoNoREQ_INVALID400/422Field/schema/path issue.
DOMAIN_REJECTEDNoNo/RateRULE_VIOLATION409/422Rule/state violation.
AUTHENTICATION_FAILEDNoAnomalyAUTH_FAILED401Avoid detail leakage.
AUTHORIZATION_DENIEDNoAnomalyACCESS_DENIED403Include policy ID internally.
RATE_LIMITEDLaterRateRATE_LIMITED429Retry-after if safe.
CONFLICTMaybeNo/RateCONFLICT409Optimistic conflict may retry.
DEPENDENCY_TIMEOUTMaybeYes if SLODEPENDENCY_TIMEOUT504Requires timeout budget.
DEPENDENCY_UNAVAILABLEMaybeYesDEPENDENCY_UNAVAILABLE503Circuit/degrade.
DATA_INTEGRITY_BROKENNoYesINTERNAL_DATA_ERROR500Preserve evidence.
PROGRAMMER_BUGNoYesINTERNAL_ERROR500Fix code.
PLATFORM_UNHEALTHYNo localYesSERVICE_UNAVAILABLE503Readiness/shutdown.
UNKNOWN_OUTCOMENo blindYesUNKNOWN_OUTCOME202/500/504Reconcile/idempotency.
FATAL_RUNTIMENoYesSERVICE_FAILURE500/503Crash/restart likely.

Catatan: HTTP status hanya contoh boundary. Untuk message processing, category yang sama bisa dipetakan ke ack, nack, retry, dead-letter, atau quarantine.


13. Java Representation: Category, Decision, Evidence

Kita tidak perlu langsung membuat framework kompleks. Minimal, pisahkan tiga hal:

  1. category — error ini jenis apa;
  2. decision — sistem harus melakukan apa;
  3. evidence — informasi apa yang harus direkam.
public enum ErrorCategory {
    INPUT_INVALID,
    DOMAIN_REJECTED,
    AUTHENTICATION_FAILED,
    AUTHORIZATION_DENIED,
    RATE_LIMITED,
    CONFLICT,
    DEPENDENCY_TIMEOUT,
    DEPENDENCY_UNAVAILABLE,
    DATA_INTEGRITY_BROKEN,
    PROGRAMMER_BUG,
    PLATFORM_UNHEALTHY,
    UNKNOWN_OUTCOME,
    FATAL_RUNTIME
}
public enum RecoveryAction {
    REJECT,
    RETRY_WITH_BUDGET,
    DEGRADE,
    COMPENSATE,
    QUARANTINE,
    ESCALATE,
    FAIL_FAST,
    RECONCILE
}
public record ErrorDecision(
    ErrorCategory category,
    RecoveryAction action,
    String publicCode,
    boolean clientVisible,
    boolean alertable,
    boolean auditRelevant
) {}

Classifier sederhana:

public final class ErrorClassifier {

    public ErrorDecision classify(Throwable error) {
        if (error instanceof RequestValidationException) {
            return new ErrorDecision(
                ErrorCategory.INPUT_INVALID,
                RecoveryAction.REJECT,
                "REQ_INVALID",
                true,
                false,
                false
            );
        }

        if (error instanceof CaseAlreadyClosedException) {
            return new ErrorDecision(
                ErrorCategory.DOMAIN_REJECTED,
                RecoveryAction.REJECT,
                "CASE_ALREADY_CLOSED",
                true,
                false,
                true
            );
        }

        if (error instanceof DependencyFailureException dependencyFailure) {
            return new ErrorDecision(
                dependencyFailure.retryable()
                    ? ErrorCategory.DEPENDENCY_TIMEOUT
                    : ErrorCategory.DEPENDENCY_UNAVAILABLE,
                dependencyFailure.retryable()
                    ? RecoveryAction.RETRY_WITH_BUDGET
                    : RecoveryAction.DEGRADE,
                "DEPENDENCY_FAILURE",
                false,
                true,
                false
            );
        }

        if (error instanceof Error) {
            return new ErrorDecision(
                ErrorCategory.FATAL_RUNTIME,
                RecoveryAction.FAIL_FAST,
                "SERVICE_FAILURE",
                false,
                true,
                false
            );
        }

        return new ErrorDecision(
            ErrorCategory.PROGRAMMER_BUG,
            RecoveryAction.ESCALATE,
            "INTERNAL_ERROR",
            false,
            true,
            false
        );
    }
}

Ini bukan desain final. Part berikutnya akan membahas Throwable dan hierarchy resmi Java. Untuk saat ini, fokusnya adalah memisahkan classification dari transport mapping.


14. Boundary Mapping: Satu Category, Banyak Boundary

Jangan mengikat taxonomy internal ke HTTP saja.

Contoh mapping:

CategoryHTTPMessage ConsumerBatch JobAudit
INPUT_INVALID400/422dead-letter if producer bugreject rowmaybe
DOMAIN_REJECTED409/422ack + business rejection eventmark rejectedyes
DEPENDENCY_TIMEOUT504retry with backoffretry itemno
DATA_INTEGRITY_BROKEN500quarantinehalt batchyes
UNKNOWN_OUTCOME202/504reconcile before retrymanual reviewyes
FATAL_RUNTIME503/crashstop consumerfail jobno

Principle:

Internal category should be stable. Boundary mapping can differ by protocol and product contract.


15. Observability Implication per Category

Setiap category membutuhkan telemetry berbeda.

CategoryLogMetricTraceAlert
Input invalidsample/rate-limitedcount by endpoint/codespan status usually not error for expected rejectionanomaly only
Domain rejectedaudit-friendly eventcount by rulespan attribute rule IDanomaly only
Dependency timeoutstructured error with dependency/attemptlatency/error rate by dependencydownstream span erroryes if SLO impacted
Data integrity brokenhigh-fidelity eventcount by invarianttrace full pathimmediate
Programmer bugstack tracecount by exception/categoryspan erroryes
Unknown outcomehigh-fidelity + reconciliation keycount unknown outcomesmark uncertaintyyes
Fatal runtimeprocess-level evidenceJVM/process metricmaybe incompletecritical

Anti-pattern umum:

metric tag: exception.message

Masalah: cardinality meledak karena message sering mengandung ID, timestamp, path, atau value input.

Lebih aman:

error.category=DEPENDENCY_TIMEOUT
error.code=DEPENDENCY_FAILURE
dependency.name=identity-service
operation=validateToken

16. Decision Tree Praktis

Gunakan decision tree berikut saat melihat error.


17. Anti-Patterns Taxonomy

17.1 Treating All Exceptions as 500

@ExceptionHandler(Exception.class)
ResponseEntity<ErrorResponse> handle(Exception e) {
    return ResponseEntity.status(500)
        .body(new ErrorResponse("INTERNAL_ERROR"));
}

Masalah:

  • validation/domain/policy menjadi internal error;
  • client tidak tahu cara memperbaiki request;
  • metric error rate tercemar oleh expected rejection;
  • alert bisa noisy;
  • audit reason hilang.

17.2 Treating All RuntimeException as Programmer Bug

Banyak domain/library memakai unchecked exception untuk failure yang valid secara bisnis atau operasional. RuntimeException adalah mekanisme bahasa, bukan taxonomy produksi.

17.3 Treating Timeout as Safe Failure

Timeout sering berarti outcome tidak diketahui.

Timeout after sending command != command failed.

17.4 Catch, Log, and Continue

try {
    publishCaseApproved(event);
} catch (Exception e) {
    log.warn("Failed to publish", e);
}

Jika publish event adalah bagian dari contract consistency, continue silently menciptakan partial commit.

17.5 Public Error Code from Class Name

com.company.case.CaseAlreadyClosedException

Class name adalah implementation detail. Public error code harus stabil walau class di-refactor.


18. Regulatory/Case Management Lens

Untuk sistem enforcement lifecycle, taxonomy error harus mendukung defensibility.

Pertanyaan penting:

  • Apakah error memengaruhi hak subjek kasus?
  • Apakah rejection perlu bisa diaudit?
  • Apakah automated decision perlu reason code?
  • Apakah partial failure bisa membuat enforcement action tidak sah?
  • Apakah retry bisa menghasilkan notice ganda?
  • Apakah fallback bisa mengubah outcome hukum/proses?
  • Apakah log cukup untuk membuktikan siapa melakukan apa, kapan, berdasarkan rule apa?

Contoh:

Failure:
  Generate enforcement notice gagal setelah case status berubah menjadi NOTICE_ISSUED.

Taxonomy:
  DATA_CONSISTENCY / PARTIAL_COMMIT / UNKNOWN_EXTERNAL_EFFECT

Wrong handling:
  Return 500 and let user click again.

Better handling:
  Store outbox command, expose pending generation state, reconcile document generation, prevent duplicate notice, audit transition and recovery.

Di sistem regulatori, “graceful” bukan berarti selalu menyembunyikan error dari user. Kadang graceful berarti menolak aksi dengan jelas agar keputusan tidak cacat.


19. Practice: 90-Minute Taxonomy Drill

Ambil satu service Java nyata atau imajiner. Misalnya CaseActionService.

Step 1 — Daftar Failure Scenario

Buat minimal 20 scenario:

1. User mengirim action tidak dikenal.
2. Case ID valid format, tetapi case tidak ditemukan.
3. Case CLOSED dicoba escalate.
4. Officer tidak punya permission.
5. Database timeout setelah query dikirim.
6. Database deadlock saat update status.
7. Event publish gagal setelah DB commit.
8. Downstream identity service 503.
9. Config rule missing.
10. JVM OutOfMemoryError.
...

Step 2 — Isi Matrix

ScenarioOriginRetry?ScopeConsistencyPublic CodeEvidence
Case CLOSED escalateDomainNoRequestNo mutationCASE_CLOSEDcaseId, status, ruleId
DB deadlockDependency/DataMaybeRequestRolled backCONFLICT_RETRYABLEtxId, table, attempt
Event publish after commit failsDependencyNot blindWorkflowPartial commitACTION_PENDINGoutboxId, caseId

Step 3 — Tentukan Wrong Recovery

Untuk setiap scenario, tulis recovery yang salah.

Contoh:

Scenario: identity service timeout.
Wrong recovery: retry 5x tanpa timeout budget di request thread.
Why wrong: memperbesar latency, memakan thread, memperburuk outage.

Step 4 — Tentukan Telemetry Minimal

Untuk setiap scenario, tentukan:

  • log event name;
  • metric name/tag;
  • trace attributes;
  • alert rule jika perlu;
  • audit event jika perlu.

20. Self-Correction Checklist

Gunakan checklist ini saat review code error handling.

[ ] Apakah error category menjawab aksi, bukan hanya nama layer?
[ ] Apakah domain rejection dipisah dari programmer bug?
[ ] Apakah input contract error dipisah dari domain rule error?
[ ] Apakah dependency timeout diperlakukan sebagai possible unknown outcome?
[ ] Apakah retry hanya dilakukan jika operasi idempotent atau aman?
[ ] Apakah partial commit punya recovery/reconciliation path?
[ ] Apakah public error code stabil dan tidak bergantung pada class name?
[ ] Apakah error response tidak membocorkan stack trace/SQL/secret?
[ ] Apakah metric tag tidak memakai message/value dengan cardinality tinggi?
[ ] Apakah log cukup untuk ownership dan debugging?
[ ] Apakah data integrity error tidak di-fix silently?
[ ] Apakah fatal runtime error tidak diperlakukan sebagai business exception?

21. Mini Design Exercise

Desain taxonomy untuk operasi berikut:

CaseDecision submitDecision(String caseId, String actorId, DecisionRequest request)

Minimal hasil:

  1. 12 failure scenario;
  2. category untuk masing-masing;
  3. public error code;
  4. retry decision;
  5. audit relevance;
  6. observability fields.

Contoh jawaban sebagian:

ScenarioCategoryPublic CodeRetryAudit
request.decisionType tidak dikenalINPUT_INVALIDINVALID_DECISION_TYPENoNo
Actor bukan assigned officerAUTHORIZATION_DENIEDACCESS_DENIEDNoYes
Case sudah closedDOMAIN_REJECTEDCASE_CLOSEDNoYes
Optimistic lock conflictCONFLICTCASE_VERSION_CONFLICTMaybeNo
Document service timeout after commandUNKNOWN_OUTCOMEDOCUMENT_GENERATION_PENDINGReconcileYes

22. Ringkasan

Taxonomy error bukan dokumentasi tambahan. Ia adalah decision system.

Kita harus membedakan:

  • fault, error, dan failure;
  • programmer error, domain error, input error, dependency error, platform error, runtime error, policy error, dan data integrity error;
  • retryable, non-retryable, compensatable, degradable, quarantinable, fatal, dan unknown outcome;
  • internal exception, public error response, log, metric, trace, audit event;
  • local request failure dan cascading failure.

Prinsip paling penting:

Jangan mendesain error berdasarkan class exception saja. Desain error berdasarkan aksi yang benar, evidence yang dibutuhkan, dan kontrak yang harus dijaga.

Di part berikutnya kita masuk ke model resmi Java: Throwable, Exception, RuntimeException, Error, checked exception, unchecked exception, cause chain, stack trace, suppressed exception, dan konsekuensinya untuk desain produksi.

Lesson Recap

You just completed lesson 03 in start here. 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.