v1.0
Internal engineering handbook

Backend Engineering Standard

Panduan teknis untuk membangun layanan backend yang konsisten, aman, observable, dan siap berjalan di production.

Mulai self-check 20 bab · 5 technology stacks
20Standard
chapters
05Backend
stacks
37Quality
checks
Fokus standarCode qualityAPI contractDatabaseSecurityObservabilityProduction
01
Core standard

Overview

Satu baseline untuk menghasilkan layanan backend yang konsisten, mudah dites, mudah dideploy, aman, observable, dan mudah dirawat.

CoreSecurityRuntime
Prinsip utama

Standardisasi bukan menyeragamkan framework. Standardisasi memastikan perilaku operasional, kualitas kode, kontrak API, dan readiness setiap layanan dapat diprediksi.

Architecture

Struktur proyek

Pisahkan transport, business logic, dan akses data dengan dependency yang jelas.

Quality

Coding convention

Gunakan formatter, linter, strict typing, dan naming yang otomatis diperiksa CI.

Contract

API response

Gunakan envelope, error code, pagination, dan correlation ID yang konsisten.

Reliability

Error handling

Petakan error domain ke status HTTP; jangan bocorkan stack trace atau detail internal.

Security

Validation

Validasi seluruh input di boundary sebelum masuk ke use case atau service.

Observability

Logging

Log terstruktur, punya request ID, level yang tepat, dan bebas data sensitif.

Runtime

Config & env

Konfigurasi lewat environment; fail fast saat variable wajib tidak tersedia.

Security

Security baseline

Least privilege, secret management, dependency scan, rate limit, dan audit trail.

Quality

Testing

Prioritaskan unit dan integration test pada jalur bisnis kritis serta kontrak API.

Production

Deployment readiness

Build reproducible, healthcheck, migration aman, observability, dan rollback plan.

Modern backend baseline

  • Twelve-factor dan stateless application

  • Graceful shutdown serta readiness/liveness check

  • Structured logging, metrics, tracing, dan correlation ID

  • Centralized error format dan strict typing jika tersedia

  • Dependency injection dan config melalui environment

  • Secure secret handling dan automated test

  • CI menjalankan lint, test, build, dan security scan

  • Container-first deployment, API versioning, dokumentasi standar, dan Bruno collection

02
Clean & maintainable code

Code Standard

Standar penulisan kode backend agar konsisten, mudah dibaca, mudah diuji, mudah dikembangkan, dan tetap sederhana tanpa over-engineering.

CodeArchitectureQuality
Problem dahulu, pattern kemudian

Design pattern adalah referensi solusi untuk masalah desain yang berulang, bukan target implementasi. Gunakan pattern hanya ketika membuat kode lebih jelas, testable, dan mudah berubah. Untuk problem sederhana, pilih solusi paling sederhana yang tetap rapi dan scalable.

Readability

Clean code

Kode harus mengungkapkan maksudnya melalui struktur, nama, dan alur yang jelas tanpa komentar berlebihan.

Consistency

Naming convention

Gunakan nama yang spesifik dan konsisten; class berupa noun, function berupa verb, boolean memakai is/has/can.

Focus

Function size

Function melakukan satu pekerjaan pada satu level abstraksi. Pecah ketika alur sulit dibaca atau diuji.

SRP

Single Responsibility

Satu module atau class memiliki satu alasan utama untuk berubah dan tidak mencampur banyak concern.

Architecture

Separation of Concerns

Pisahkan request handling, validasi, business rule, data access, dan external integration.

Reuse

Reusable component

Ekstrak komponen ketika perilaku benar-benar berulang dan stabil, bukan berdasarkan kemiripan sesaat.

DRY

Avoid duplicate code

Hapus duplikasi pengetahuan dan business rule; jangan memaksakan abstraksi untuk dua baris yang kebetulan sama.

Quality

Testable & maintainable

Business logic dapat diuji tanpa database, network, clock, atau external service yang nyata.

Aturan penulisan kode

AreaStandarHindari
NamingNama menjelaskan domain dan maksudSingkatan ambigu seperti data1, temp, mgr
Function/methodSatu tujuan, input jelas, return type konsistenFunction panjang dengan banyak side effect
ParameterGunakan object/DTO untuk parameter yang saling terkaitBanyak positional parameter dan boolean flag
Hardcoded valuePindahkan konfigurasi ke config atau environmentURL, timeout, credential, dan environment di source
Magic number/stringGunakan named constant, enum, atau value objectAngka/status string tanpa konteks
Duplicate ruleSatu sumber untuk business rule yang samaCopy-paste validasi atau perhitungan
InterfaceDefinisikan contract pada boundary yang perlu diganti atau diujiInterface satu-implementasi tanpa alasan
DependencyInject dependency dari luar melalui constructor/factoryMembuat database/client eksternal di business logic
FolderStruktur konsisten berdasarkan module/domain dan layerFile sejenis tersebar tanpa aturan
CommentsJelaskan alasan, constraint, dan trade-offKomentar yang hanya mengulang kode
Struktur layer umum
Controller / Handler
↓
Request Validation / DTO
↓
Service / Usecase
↓
Repository / Data Access
↓
Database / External API

Tanggung jawab setiap layer

LayerTanggung jawabTidak boleh
Controller / HandlerTerima request, panggil use case, map responseBusiness logic atau query database
Validation / DTOValidasi bentuk, tipe, format, dan boundary inputMenjalankan workflow bisnis
Service / UsecaseOrkestrasi business rule dan transaction boundaryBergantung pada detail HTTP/framework
Repository / Data AccessQuery dan mapping model persistenceMenentukan keputusan bisnis
External adapterTerjemahkan contract aplikasi ke API/library eksternalMembocorkan format vendor ke domain
Domain/modelRepresentasikan aturan dan invariant bisnisBergantung langsung pada transport atau database

Design pattern dan kapan digunakan

PatternGunakan ketikaJangan dipaksakan ketika
RepositoryAkses database perlu dipisahkan dari business logic atau diganti saat testORM sederhana sudah cukup dan wrapper hanya meneruskan method
ServiceBusiness logic/use case perlu dipisahkan agar controller tetap tipisService hanya menjadi pass-through tanpa aturan atau orkestrasi
FactoryPembuatan object memiliki variasi, dependency, atau aturan konstruksi khususObject dapat dibuat jelas dengan constructor biasa
StrategyAda beberapa algoritma atau rule yang dapat dipilih dan ditukarHanya ada satu if sederhana yang stabil
AdapterExternal API/library mempunyai contract berbeda dari contract internalFormat dependency sudah sesuai dan tidak perlu isolasi
FacadeConsumer memerlukan satu pintu sederhana ke beberapa proses atau subsystem kompleksFacade hanya menambah nama baru tanpa menyederhanakan apa pun
Observer / Pub-SubNotifikasi, audit log, atau async flow perlu merespons event secara terpisahProses harus sinkron, atomik, dan urut dalam satu transaksi
BuilderObject/request kompleks memiliki banyak opsi dan konstruksi bertahapObject kecil dapat dibuat dengan DTO/constructor
Dependency InjectionClass perlu bergantung pada contract agar implementasi dapat diganti dan diujiDependency berupa value sederhana yang tidak memiliki perilaku
MVC / layered architectureRequest handling, business logic, dan data access perlu dipisahkanLayer tambahan tidak memiliki tanggung jawab yang berbeda
Dependency Injection untuk kode yang testable
interface UserRepository {
  findByEmail(email: string): Promise<User | null>;
  save(user: User): Promise<User>;
}

class RegisterUser {
  constructor(private readonly users: UserRepository) {}

  async execute(input: RegisterUserInput): Promise<User> {
    const existing = await this.users.findByEmail(input.email);
    if (existing) throw new EmailAlreadyUsedError();

    return this.users.save(User.create(input));
  }
}

// Unit test dapat meng-inject in-memory repository tanpa database nyata.

Konsistensi lintas endpoint

ConcernStandarLokasi implementasi
ValidationSchema, format error, dan sanitasi konsistenRequest/DTO validator
ResponseEnvelope, pagination, message, dan metadata konsistenResponse mapper/resource
Error handlingError domain dipetakan terpusat ke status dan error codeGlobal exception/error handler
LoggingStructured log, request ID, level tepat, tanpa secretLogger abstraction/middleware
ConfigurationTyped config divalidasi sekali saat startupConfig module
External serviceTimeout, retry, mapping, dan error vendor diisolasiAdapter/client module

Checklist menghindari over-engineering

  • Mulai dari kebutuhan dan perubahan yang benar-benar terjadi, bukan kemungkinan yang belum jelas.

  • Jangan membuat interface, base class, factory, atau generic hanya untuk satu implementasi tanpa kebutuhan test/boundary.

  • Abstraksi harus mengurangi pengetahuan yang tersebar, bukan sekadar memindahkan kode ke file lain.

  • Controller/handler tetap tipis dan business logic tidak menumpuk pada transport layer.

  • Query database tidak tersebar di controller, handler, job, atau utility tanpa data access boundary.

  • Pilih composition dan function sederhana sebelum inheritance atau hierarchy yang dalam.

  • Hapus pattern ketika kompleksitasnya lebih besar daripada masalah yang diselesaikan.

  • Pastikan solusi masih mudah dipahami engineer berikutnya tanpa penjelasan lisan khusus.

Referensi eksternalDesign Patterns PDFReferensi konseptual untuk memahami tujuan, konteks, dan trade-off design pattern. Gunakan sebagai inspirasi penyelesaian masalah, bukan aturan yang harus diterapkan secara paksa.https://www.scribd.com/document/467496968/design-patterns-pdfBuka referensi
Definition of done kode backend

Kode memiliki naming dan struktur yang konsisten, controller tipis, business rule berada pada layer yang tepat, dependency dapat diganti saat test, error/response/validation/logging terpusat, serta tidak memakai pattern yang tidak memberikan manfaat nyata.

03
Fast & isolated feedback

Unit Testing

Standar pengujian unit backend untuk memverifikasi business rule secara cepat, deterministik, terisolasi, dan mudah dipahami saat terjadi kegagalan.

TestingQualityCI
Uji perilaku terkecil yang bernilai

Unit test memverifikasi satu unit perilaku—umumnya function, class, service, atau use case—tanpa menjalankan database, network, queue, filesystem, dan framework container nyata. Tujuannya memberi feedback cepat terhadap business rule, bukan mengejar angka coverage semata.

Unit test vs integration test vs end-to-end test

AspekUnit testIntegration testEnd-to-end test
ScopeSatu function/class/use caseInteraksi beberapa komponenFlow aplikasi dari entry point hingga dependency nyata
DependencyDiganti stub, fake, atau mockSebagian nyata, misalnya database testEnvironment semirip mungkin dengan production
KecepatanSangat cepat dan dijalankan seringLebih lambatPaling lambat dan lebih mahal
TujuanBusiness rule dan edge caseKontrak antar-komponenKeyakinan pada critical user journey
ContohPerhitungan biaya dan rule statusRepository dengan databaseRequest API lengkap hingga persistence
Feedback

Fast

Satu test selesai cepat dan seluruh suite unit dapat dijalankan setiap kali developer mengubah kode.

Boundary

Isolated

Tidak bergantung pada database, network, queue, file, urutan test lain, atau shared mutable state.

Reliable

Deterministic

Input yang sama selalu menghasilkan outcome yang sama; waktu, random, UUID, dan environment dikontrol.

Documentation

Readable

Nama dan struktur test menjelaskan rule bisnis, kondisi, tindakan, serta hasil yang diharapkan.

Repeatable

Independent

Setiap test dapat berjalan sendiri, paralel, atau dalam urutan apa pun tanpa memengaruhi hasil.

Maintainable

Behavior-focused

Assertion memeriksa observable behavior dan kontrak, bukan private method atau urutan internal yang rapuh.

Struktur Arrange–Act–Assert

TahapIsi
ArrangeSiapkan input, subject under test, dependency pengganti, serta expected result.
ActJalankan satu perilaku utama yang sedang diuji.
AssertPeriksa output, perubahan state, error domain, atau side effect yang menjadi kontrak.

Kasus minimum yang perlu diuji

  • Happy path untuk memastikan rule menghasilkan outcome yang benar.

  • Validation failure dan input kosong, salah format, atau di luar rentang.

  • Boundary value tepat sebelum, pada, dan setelah batas business rule.

  • Dependency mengembalikan not found, conflict, timeout, atau error lain yang dipetakan oleh unit.

  • Authorization atau state transition yang diizinkan dan ditolak.

  • Idempotency dan duplicate request bila unit menghasilkan side effect.

  • Pembulatan, timezone, tanggal pergantian hari, atau expiry jika relevan.

  • Regression test untuk setiap bug yang telah ditemukan dan diperbaiki.

Test double dan penggunaannya

JenisGunakan ketikaPerhatian
StubDependency perlu mengembalikan nilai atau error tertentuJangan menambahkan verifikasi interaksi yang tidak dibutuhkan.
FakePerlu implementasi ringan seperti in-memory repositoryPerilakunya harus tetap mengikuti contract implementasi nyata.
MockInteraksi dengan dependency adalah bagian penting dari kontrakHindari mock setiap method karena test menjadi terikat implementasi.
SpyPerlu memeriksa side effect seperti event diterbitkan sekaliGunakan hanya untuk interaksi yang observable dan penting.
Contoh unit test service
import { describe, expect, it, vi } from "vitest";

describe("RegisterUser", () => {
  it("menolak email yang sudah terdaftar", async () => {
    // Arrange
    const users = {
      findByEmail: vi.fn().mockResolvedValue({ id: "usr_existing" }),
      save: vi.fn(),
    };
    const service = new RegisterUser(users);

    // Act
    const action = service.execute({ email: "user@bit.co.id" });

    // Assert
    await expect(action).rejects.toThrow(EmailAlreadyUsedError);
    expect(users.save).not.toHaveBeenCalled();
  });
});

Naming dan struktur file

AreaStandar
Nama testNyatakan kondisi dan hasil: should_reject_registration_when_email_already_exists atau bahasa yang konsisten di proyek.
GroupingKelompokkan berdasarkan unit dan behavior, bukan berdasarkan urutan eksekusi.
LokasiGunakan konvensi stack secara konsisten: berdampingan dengan source atau pada folder tests/unit.
FixtureGunakan factory/builder kecil untuk data valid; override hanya field yang relevan dengan skenario.
AssertionBuat assertion spesifik dan minimal sehingga alasan kegagalan langsung terlihat.

Standar per stack

StackTool umumStandar unit test
LaravelPHPUnit atau PestTest service/domain tanpa boot framework bila tidak diperlukan; gunakan Mockery serta Laravel fakes untuk boundary yang relevan.
Node.js TypeScriptVitest, Jest, atau node:testGunakan strict typing, inject dependency, reset mock setelah test, dan kontrol timer/clock saat menguji waktu.
Gotesting packageGunakan file *_test.go, table-driven test, subtest, interface pada consumer boundary, serta go test ./....
Spring BootJUnit Jupiter, AssertJ, MockitoInstansiasi class secara langsung untuk unit test; jangan memakai @SpringBootTest jika Spring context tidak dibutuhkan.
Pythonpytest dan unittest.mockGunakan fixture kecil, monkeypatch hanya pada boundary, parametrization untuk variasi input, dan hindari I/O nyata.

Coverage dan CI

  • Coverage membantu menemukan kode yang belum diuji, tetapi tidak membuktikan kualitas assertion atau kelengkapan skenario.

  • Prioritaskan coverage tinggi pada business rule, authorization, perhitungan, state transition, dan error mapping yang kritis.

  • Jangan menulis test tanpa nilai hanya untuk mencapai 100% coverage atau menguji getter/setter sederhana.

  • Tetapkan threshold CI berdasarkan baseline dan risiko proyek; perubahan baru tidak boleh menurunkan coverage tanpa alasan yang direview.

  • Jalankan unit test pada setiap merge request dan hentikan merge bila test gagal atau flaky test belum ditangani.

  • Pisahkan laporan unit, integration, dan end-to-end agar kegagalan mudah diklasifikasikan.

Anti-pattern yang harus dihindari

  • Unit test mengakses database, Redis, filesystem, network, atau API eksternal nyata.

  • Satu test memverifikasi terlalu banyak behavior yang tidak berkaitan.

  • Mock private method atau seluruh internal call sehingga refactor aman justru merusak test.

  • Menggunakan sleep, waktu sistem, random, UUID, atau urutan eksekusi tanpa kontrol.

  • Menangkap exception tanpa memastikan jenis dan error code yang diharapkan.

  • Test saling berbagi state dan hanya lulus bila dijalankan dalam urutan tertentu.

  • Menghapus atau skip test gagal tanpa tiket, alasan, owner, dan batas waktu perbaikan.

  • Menyalin setup besar ke setiap test daripada memakai factory/builder atau helper yang tetap eksplisit.

Referensi eksternalGo — Add a TestReferensi resmi tentang konvensi file test, test function, dan menjalankan unit test menggunakan Go testing package.https://go.dev/doc/tutorial/add-a-testBuka referensi Referensi eksternalSpring Boot TestingReferensi resmi tentang unit test dengan dependency injection dan pemisahannya dari test yang membutuhkan ApplicationContext.https://docs.spring.io/spring-boot/reference/testing/spring-applications.htmlBuka referensi
Definition of done unit testing

Business rule baru dan berubah memiliki unit test untuk jalur sukses, gagal, serta boundary yang relevan; test cepat, deterministik, independen, bebas I/O nyata, mudah dipahami, lulus lokal dan CI, serta regression test ditambahkan untuk bug yang diperbaiki.

04
JIRA-driven delivery

Alur Kerja GitLab BIT

Standar kontribusi backend di GitLab BIT agar setiap branch, commit, merge request, dan proses review dapat ditelusuri kembali ke tiket JIRA.

GitLabJIRAReview
JIRA menjadi referensi utama

Setiap perubahan backend wajib memiliki tiket JIRA. Gunakan ID tiket yang sama pada nama branch, commit message, dan merge request agar konteks pekerjaan serta riwayat perubahannya mudah ditelusuri.

Konvensi penamaan

ArtefakFormatContoh
Branch<JIRA-ID>-<scope>-<deskripsi-singkat>PRIMA-438-be-general-daftar-antrian
Commit<JIRA-ID> [BE] <ringkasan perubahan>PRIMA-438 [BE] General & Daftar Antrian
Merge request<JIRA-ID> [BE] <ringkasan perubahan>PRIMA-438 [BE] General & Daftar Antrian

Aturan branch dan commit

  • Buat satu branch untuk satu tiket JIRA atau satu scope perubahan yang jelas.

  • Gunakan ID tiket dengan huruf kapital, lalu tulis scope dan deskripsi branch dalam lowercase kebab-case.

  • Gunakan scope be untuk perubahan backend agar jenis pekerjaan mudah dikenali.

  • Commit harus fokus, dapat dipahami, dan tidak mencampur perubahan lain yang tidak berkaitan dengan tiket.

  • Awali commit message dengan ID JIRA dan penanda [BE], kemudian jelaskan hasil perubahan secara ringkas.

  • Jangan memasukkan secret, credential, file environment lokal, hasil build, atau dependency ke commit.

Membuat branch dan mengirim perubahan
# Mulai dari branch utama yang terbaru
git checkout main
git pull origin main

# Buat branch berdasarkan tiket JIRA
git checkout -b PRIMA-438-be-general-daftar-antrian

# Periksa perubahan sebelum commit
git status
git diff

# Tambahkan file yang memang termasuk scope tiket
git add path/to/changed-file
git commit -m "PRIMA-438 [BE] General & Daftar Antrian"

# Push branch ke GitLab BIT
git push -u origin PRIMA-438-be-general-daftar-antrian

Isi merge request

BagianStandar minimum
JudulGunakan ID JIRA, scope [BE], dan ringkasan yang sama jelasnya dengan commit.
DeskripsiJelaskan tujuan, perubahan utama, dampak, cara pengujian, serta catatan migration/config bila ada.
JIRACantumkan link tiket dan pastikan scope merge request sesuai acceptance criteria.
Bukti pengujianSertakan hasil lint, test, build, API collection, screenshot, atau log yang relevan.
RisikoJelaskan breaking change, perubahan kontrak API, database migration, dependency, dan rollback plan.
ReviewerTetapkan senior atau lead sebagai reviewer; gunakan reviewer sesama tim yang memahami area tersebut bila lead berhalangan.

Alur review dan merge

  1. 1

    Developer menyelesaikan self-check, memperbarui dokumentasi/API collection, lalu memastikan lint, test, dan build berhasil.

  2. 2

    Developer push branch dan membuat merge request yang terhubung dengan tiket JIRA.

  3. 3

    Senior atau lead meninjau ketepatan solusi, kualitas kode, keamanan, test, serta dampak operasional.

  4. 4

    Jika lead tidak dapat melakukan review, review dapat dilakukan oleh senior atau anggota tim lain yang memahami scope perubahan.

  5. 5

    Developer menindaklanjuti komentar review dan tidak menandai diskusi selesai sebelum perbaikannya tersedia atau alasannya disepakati.

  6. 6

    Merge hanya dilakukan setelah review disetujui, pipeline wajib lulus, dan tidak ada diskusi penting yang belum selesai.

  7. 7

    Setelah merge, pastikan status tiket JIRA diperbarui mengikuti workflow proyek dan branch dibersihkan sesuai kebijakan repository.

Review tetap wajib

Ketiadaan lead tidak boleh membuat perubahan langsung di-merge tanpa review. Minta senior atau rekan satu tim yang kompeten pada area tersebut untuk meninjau perubahan dan memberikan approval.

Referensi eksternalGitLab BITRepository dan merge request internal BIT. Akses mengikuti akun serta permission proyek masing-masing.https://git.bitgroup.devBuka referensi
Definition of done

Branch, commit, dan merge request terhubung ke tiket JIRA; perubahan tetap dalam scope; pemeriksaan otomatis lulus; dokumentasi dan test diperbarui; serta review telah disetujui oleh lead, senior, atau reviewer tim yang kompeten.

05
Contract & transport

API Standard

Kontrak HTTP yang eksplisit membuat integrasi lebih aman, mudah di-debug, dan kompatibel lintas layanan.

APICore
Input

Request

Gunakan JSON, ISO 8601 UTC, validasi schema, dan header X-Request-ID.

Collection

Query

Filter eksplisit, sort allowlist, serta page dan per_page dengan batas maksimum.

Lifecycle

Versioning

Versi mayor pada URL seperti /api/v1; perubahan breaking wajib versi baru.

Mandatory

Documentation

Dokumentasi standar dan Bruno collection wajib diperbarui dalam MR yang sama. OpenAPI/Swagger terpisah opsional; jika dipakai, kontraknya harus sinkron.

Envelope

Response message

Sertakan message yang ringkas, konsisten, mudah dipahami client, dan tidak membocorkan detail internal.

Response sukses
{
  "success": true,
  "message": "Data pengguna berhasil diambil",
  "data": {
    "id": "usr_01J8...",
    "name": "Nadia Putri"
  },
  "meta": {
    "request_id": "req_01J9...",
    "timestamp": "2026-09-02T08:15:30Z"
  }
}
Response error
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Permintaan tidak valid",
    "details": [
      { "field": "email", "message": "Format email tidak valid" }
    ]
  },
  "meta": { "request_id": "req_01J9..." }
}

HTTP status code

StatusGunakan untukCatatan
200 OKRead/update berhasilResponse memiliki data
201 CreatedResource berhasil dibuatSertakan Location bila relevan
204 No ContentDelete/action tanpa bodyBody harus kosong
400 Bad RequestRequest tidak dapat diprosesBukan untuk error server
401 / 403Belum login / tidak berwenangBedakan authentication dan authorization
404 / 409Tidak ditemukan / konflik stateGunakan error code domain
422 UnprocessableValidasi field gagalSertakan detail field
500 / 503Error internal / dependency unavailableLog detail, kirim pesan generik

Aturan collection endpoint

  • Pagination: page, per_page (default 20, maksimum 100), total, dan total_pages.

  • Filtering: gunakan nama field eksplisit; jangan menerima raw SQL dari client.

  • Sorting: format sort=-created_at,name dan batasi field yang diizinkan.

  • Setiap response membawa request_id agar log antar-service dapat dikorelasikan.

06
Collection as code

API Collection

Standar dokumentasi dan pengujian manual API berbasis file dengan Bruno sebagai tujuan utama migrasi dari Postman.

APIBrunoGit
Bruno menjadi standar utama

Semua endpoint backend wajib memiliki API collection. Selama masa transisi Postman masih boleh digunakan, tetapi setiap endpoint baru atau yang berubah harus mulai tersedia di Bruno dan disimpan bersama repository sebagai API collection as code.

Validation

Manual testing

Engineer dapat menjalankan request, memeriksa response, dan mengulang skenario tanpa menyusun request dari awal.

Documentation

Dokumentasi endpoint

Method, URL, auth, header, payload, contoh response, dan assertion terdokumentasi dekat dengan source code.

Developer experience

Onboarding

Developer baru cukup clone repository, membuka folder Bruno, memilih environment, lalu menjalankan collection.

Release

Pre-deploy check

Collection menjadi smoke test kontrak utama sebelum deploy dan dapat dijalankan melalui Bruno CLI di CI.

Git workflow

Kolaborasi

Perubahan endpoint dan collection direview dalam pull request yang sama agar backend dan consumer selalu sinkron.

Contract

Communication

Collection menjadi contoh executable yang dapat digunakan saat berkomunikasi dengan frontend atau service lain.

Alasan peralihan dari Postman ke Bruno

AspekPostman pada masa transisiStandar Bruno
PenyimpananCollection sering berpusat di workspace/exportRequest tersimpan sebagai file teks di repository
ReviewPerubahan export JSON sulit dibacaDiff `.bru` kecil dan mudah direview melalui Git
OwnershipDapat terpisah dari lifecycle source codeCollection mengikuti branch, tag, dan release aplikasi
KolaborasiSinkronisasi bergantung workspace atau exportClone, branch, commit, pull request, dan merge
AutomationDapat dijalankan melalui tooling PostmanCollection Runner dan Bruno CLI `bru run`
TargetTetap boleh untuk collection lama sementaraWajib untuk endpoint baru dan tujuan akhir migrasi
Struktur folder Bruno collection
docs/03-api/bruno/
├── bruno.json
├── collection.bru
├── .env.example
├── environments/
│   ├── local.bru
│   ├── staging.bru
│   └── production.bru.example
├── auth/
│   ├── folder.bru
│   ├── login.bru
│   └── refresh-token.bru
├── users/
│   ├── folder.bru
│   ├── list-users.bru
│   ├── detail-user.bru
│   ├── create-user.bru
│   ├── update-user.bru
│   └── delete-user.bru
└── products/
    ├── folder.bru
    ├── list-products.bru
    ├── detail-product.bru
    ├── create-product.bru
    ├── update-product.bru
    └── delete-product.bru

Standar struktur dan penamaan

AreaStandarContoh
RootSatu folder `docs/03-api/bruno/` per backend repositorydocs/03-api/bruno/bruno.json
GroupingKelompokkan berdasarkan module atau business domainauth/, users/, products/
File requestGunakan kebab-case dan pola aksi-resourcelist-users.bru, create-user.bru
Nama requestGunakan kata kerja yang konsisten dan mudah dicariList Users, Create User
UrutanAuth sebelum request yang memerlukan token; gunakan `seq` bila runner bergantung urutanLogin → List Users
CoverageMinimal CRUD, auth, validation error, not found, dan critical business flowCreate User — Invalid Email
Environment file
# environments/local.bru
vars {
  base_url: http://localhost:3000
  access_token: {{process.env.ACCESS_TOKEN}}
  refresh_token: {{process.env.REFRESH_TOKEN}}
}

# .env.example — hanya nama variable, tanpa nilai asli
ACCESS_TOKEN=
REFRESH_TOKEN=
LOGIN_EMAIL=
LOGIN_PASSWORD=
Secret tidak boleh masuk collection

Dilarang menyimpan access token, refresh token, password, API key, cookie, client secret, atau secret production di file `.bru`. Gunakan process environment, `.env` lokal yang diabaikan Git, secret manager, atau runtime variable. Commit hanya `.env.example` dan template environment tanpa nilai rahasia.

Auth request, runtime token, dan assertion
meta {
  name: Login
  type: http
  seq: 1
}

post {
  url: {{base_url}}/api/v1/auth/login
  body: json
  auth: none
}

headers {
  content-type: application/json
  accept: application/json
}

body:json {
  {
    "email": "{{process.env.LOGIN_EMAIL}}",
    "password": "{{process.env.LOGIN_PASSWORD}}"
  }
}

script:post-response {
  const body = res.getBody();
  bru.setVar("access_token", body.data.access_token);
  bru.setVar("refresh_token", body.data.refresh_token);
}

tests {
  test("login berhasil", function () {
    expect(res.getStatus()).to.equal(200);
  });

  test("response memiliki access token", function () {
    expect(res.getBody().data.access_token).to.be.a("string");
  });
}

Script dan test

  • Gunakan auth/login request untuk memperoleh token dan simpan sementara dengan `bru.setVar()`.

  • Gunakan `{{access_token}}` pada Bearer auth request berikutnya; runtime variable tidak disimpan ke file.

  • Pre-request script hanya digunakan untuk kebutuhan dinamis seperti signature, timestamp, nonce, atau header turunan.

  • Setiap endpoint penting minimal menguji status code dan struktur response atau business field utama.

  • Tambahkan assertion untuk validation error, unauthorized, forbidden, not found, dan conflict bila relevan.

  • Hindari script kompleks; business logic tetap diuji di automated test backend, bukan dipindahkan ke collection.

Cara menjalankan collection

KebutuhanCaraStandar
Satu requestBuka Bruno, pilih environment, lalu SendPastikan environment aktif terlihat sebelum request production
Manual collectionGunakan Collection RunnerJalankan auth dan critical flow sebelum deploy
Seluruh collection`cd bruno && bru run --env local`Gunakan Bruno CLI yang dipin pada project/CI
Satu module`bru run users --env staging`Inject secret melalui environment CI
CI/CDJalankan `bru run` setelah deploy ke test environmentGagal assertion harus menggagalkan validation job

Workflow repository dan Git review

  1. 1

    Simpan folder `docs/03-api/bruno/` di repository backend dan commit bersama perubahan endpoint terkait.

  2. 2

    Developer memperbarui request, environment template, test, dan urutan collection pada branch yang sama.

  3. 3

    Reviewer memeriksa method, URL, auth, payload, variable, assertion, serta memastikan tidak ada secret di diff.

  4. 4

    Jalankan collection pada local atau staging dan lampirkan hasil bila perubahan menyentuh critical flow.

  5. 5

    Merge collection bersama source code agar branch, tag, release, dan rollback tetap memiliki kontrak yang sesuai.

Tahapan migrasi Postman ke Bruno

  1. 1

    Inventarisasi collection Postman aktif dan tentukan owner setiap module.

  2. 2

    Export collection Postman lalu import ke Bruno; jangan menyalin credential atau environment secret.

  3. 3

    Susun ulang folder berdasarkan domain dan normalisasi naming request serta variable.

  4. 4

    Pindahkan token/password ke process environment dan tambahkan `.env.example`.

  5. 5

    Tambahkan assertion untuk response penting lalu validasi melalui Collection Runner dan CLI.

  6. 6

    Review hasil migrasi melalui pull request; tandai collection Postman lama sebagai deprecated/read-only.

07
Data & persistence

Database Standard

Standar penggunaan SQL dan NoSQL oleh backend agar model data konsisten, akses aman, query terukur, dan perubahan schema dapat dikendalikan.

DatabaseSQLNoSQLMigration
Database Standard

Backend harus memilih database berdasarkan kebutuhan data dan pola akses, bukan tren. Setiap service wajib memiliki ownership data yang jelas, model dan naming yang konsisten, akses tervalidasi, query terukur, perubahan schema terkontrol, serta backup dan observability yang dapat diuji.

Migration adalah wajib

Setiap perubahan struktur database wajib dibuat sebagai migration atau versioned script, disimpan bersama source code, dan melalui code review. Larangan melakukan perubahan manual langsung di production mencakup pembuatan/perubahan/penghapusan table, collection, column, field, index, constraint, relation, view, function, serta data reference yang dibutuhkan aplikasi.

SQL vs NoSQL

AspekSQLNoSQL
Model dataTabel, row, column, dan relasi eksplisitDocument, key-value, wide-column, atau graph
Cocok untukData relasional dan aturan integritas kuatPola akses spesifik, schema fleksibel, atau skala distribusi tertentu
ConsistencyUmumnya transaksi ACID dan constraint kuatBergantung produk; dapat strong atau eventual consistency
QueryJoin dan query ad hoc lebih kuatDioptimalkan untuk access pattern yang sudah diketahui
SchemaMigration versioned dan backward-compatibleSchema tetap harus didefinisikan serta divalidasi di aplikasi
Contoh penggunaanUser, transaksi, billing, inventorySession, cache, event, catalog, activity feed
Boundary

Data ownership

Satu service menjadi pemilik data. Service lain mengakses melalui API atau event, bukan tabel secara langsung.

Consistency

Naming convention

Gunakan satu gaya penamaan untuk tabel, collection, field, primary key, foreign key, index, dan constraint.

Design

Data model

Model mengikuti kebutuhan bisnis dan access pattern; hindari menyimpan data yang sama tanpa aturan sinkronisasi.

Integrity

Validation

Validasi tipe, format, panjang, enum, dan business invariant di boundary serta constraint database bila tersedia.

Architecture

Access layer

Akses database melalui repository/data access layer; controller tidak menjalankan query secara langsung.

Security

Sensitive data

Klasifikasikan data, batasi field yang disimpan, enkripsi bila perlu, dan jangan menulis secret atau PII ke log.

Audit

Audit fields

Gunakan created_at dan updated_at secara konsisten; tambahkan deleted_at atau actor ID bila kebutuhan audit menuntutnya.

Ownership

Source of truth

Tetapkan sumber data utama dan aturan sinkronisasi untuk cache, search index, replica, atau read model.

Standar akses database dari backend

AreaStandarYang harus dihindari
ConnectionGunakan pool/client singleton, atur limit, lifetime, idle, dan graceful shutdownMembuat koneksi atau pool baru pada setiap request
QueryParameterized query, field eksplisit, pagination, timeout, dan query planRaw query dari input, SELECT *, atau query tanpa batas
IndexBuat berdasarkan filter, sort, join, dan access pattern nyataIndex berlebihan atau tanpa bukti penggunaan
TransactionBoundary singkat, commit/rollback eksplisit, dan failure path diujiNetwork call lambat di dalam transaksi
N+1Gunakan eager loading, batch, aggregate, atau data loaderQuery berulang di dalam loop
PaginationGunakan cursor untuk data besar; offset masih boleh untuk data terbatasMengambil seluruh dataset ke memory
Read/writeGunakan replica setelah memahami consistency dan replication lagMembaca data yang harus langsung konsisten dari replica
CachingTentukan TTL, invalidation, key naming, dan fallbackMenganggap cache sebagai source of truth
Wajib mencegah Connection Pool Hell

Setiap backend wajib memastikan penggunaan koneksi database tetap terkendali saat traffic normal, lonjakan beban, autoscaling, rolling deployment, dan gangguan dependency. Connection Pool Hell adalah kondisi ketika koneksi habis atau antrean memperoleh koneksi terus membesar sehingga request timeout dan retry memperparah beban. Pool/proxy terpasang saja belum memenuhi standar: budget koneksi, lifecycle, backpressure, monitoring, dan hasil pengujian wajib tersedia.

Pool aplikasi dan proxy database

LapisanTanggung jawabBatas yang wajib dipahami
Pool aplikasiGunakan ulang koneksi dan batasi pekerjaan yang memakai database dalam satu prosesPool setiap proses berbeda; jumlahkan web, worker, scheduler, replica, dan koneksi langsung
PostgreSQL → PgBouncerGunakan sebagai pilihan standar pooling eksternal ketika koneksi banyak atau sering bergantiTransaction pooling melepas koneksi setelah transaksi selesai; fitur session harus diuji kompatibilitasnya
MySQL → ProxySQLGunakan sebagai pilihan standar proxy pooling/multiplexingTransaksi aktif serta beberapa penggunaan session state dapat menonaktifkan multiplexing dan menahan koneksi backend
NoSQLGunakan pool/client driver yang sesuai produk dan topologyPgBouncer dan ProxySQL bukan solusi umum NoSQL; hitung pool per server/node sesuai perilaku driver
Connection budget — contoh, bukan angka default production
Hitung per server database tujuan.

Budget aplikasi = limit yang disetujui
                  - koneksi operasional/service lain
                  - headroom pemulihan

Contoh: 200 - 40 - 40 = 120 koneksi maksimum untuk aplikasi.

Tanpa proxy:
  web:    6 replica × 2 proses × pool_max 8 = 96
  worker: 2 replica × 1 proses × pool_max 8 = 16
  total: 112 <= 120

Jumlah replica harus mencakup puncak autoscaling + rolling surge.
Tambahkan scheduler, migration, monitoring, dan pool lain jika belum dihitung.

Dengan proxy:
  2 instance proxy × batas backend agregat 50 = 100
  jalur langsung yang diizinkan             = 10
  total koneksi ke database                 = 110 <= 120

Koneksi client ke proxy berbeda dari koneksi proxy ke database.
Batas backend agregat mencakup seluruh pool/user/database/hostgroup
yang menuju server yang sama. Ulangi perhitungan saat failover.

Budget adalah plafon, bukan target untuk selalu dipenuhi.
Tentukan pool efektif melalui pengukuran throughput dan latency.

Aturan wajib di aplikasi

  • Pada runtime long-lived, buat pool/client sekali per proses dan konfigurasi, gunakan ulang, lalu tutup setelah request/job selesai saat graceful shutdown. Jangan membuat pool per request.

  • Sesuaikan lifecycle dengan runtime: PHP request-based dan persistent connection tidak identik dengan shared pool. Hitung jumlah proses PHP/FrankenPHP dan queue worker; verifikasi pembersihan state pada worker long-lived.

  • Connection yang dipinjam wajib dikembalikan pada jalur sukses, error, timeout, dan cancellation. Gunakan finally/defer/with atau mekanisme driver; tutup cursor/result dan selesaikan commit/rollback.

  • Pisahkan connect timeout, pool-acquire timeout, query timeout, transaction deadline, dan request deadline. Budget total harus muat dalam deadline request; cancellation harus membatalkan pekerjaan dan membersihkan koneksi.

  • Batasi antrean menunggu koneksi dan concurrency request/job. Gunakan rate limit atau respons 503 saat kapasitas sementara habis; jangan menunggu tanpa batas.

  • Jangan menahan transaksi sambil memanggil API eksternal, menunggu queue, atau mengolah file. Cegah N+1, full scan tak perlu, dan transaksi idle.

  • Retry hanya error transient, dengan batas percobaan, backoff dan jitter. Hindari retry bertingkat di semua layer; pastikan write idempotent dan periksa hasil jika commit tidak pasti.

Pemilihan dan batasan proxy

PilihanYang harus dikonfigurasi/diperiksaJika tidak kompatibel
PgBouncerBedakan max_client_conn dari default_pool_size per pasangan user/database; tetapkan max_db_connections/max_user_connections sesuai topology dan hitung seluruh instance proxyUji LISTEN, session advisory lock, temporary table dan state session. Pisahkan workload tersebut ke session pool atau jalur langsung dengan budget khusus
Prepared statement PostgreSQLProtocol-level prepared statement pada transaction pooling memerlukan dukungan versi dan max_prepared_statements non-zero; SQL PREPARE/DEALLOCATE memiliki batasan berbedaUji ORM/driver dan migration tool; gunakan mode yang kompatibel untuk workload tersebut
ProxySQLBedakan koneksi frontend dari batas backend mysql_servers.max_connections. Hitung server yang sama di berbagai hostgroup/instance, dan pantau koneksi used/free serta multiplexingPeriksa transaksi panjang, temporary table, lock, dan session/user variables yang menahan koneksi. Jangan memaksa multiplexing bila merusak session semantics
Operasional proxyBatasi akses, gunakan credential/TLS sesuai kebutuhan, healthcheck, monitoring, kapasitas file descriptor, dan strategi failoverUji reconnect dengan backoff dan anggaran saat failover; proxy harus punya kapasitas dan prosedur pemulihan yang terukur

Pool tetap penuh? Cocokkan gejala dengan penyebab

GejalaPeriksaTindakan
Used connection tidak turun setelah pekerjaan selesaiConnection leak, cursor terbuka, transaksi belum selesaiPerbaiki release/close dan rollback pada seluruh jalur; uji error dan cancellation
Acquire wait tinggi, query lambat, lock meningkatQuery plan, index, N+1, blocking transaction, idle in transactionOptimalkan query dan pendekkan transaksi; batasi batch, jangan langsung membesarkan pool
Koneksi melonjak saat deploy/autoscalingJumlah proses × replica × pool, rolling surge, pool minimum, reconnect stormTurunkan pool/concurrency atau cap replica; lakukan rollout bertahap sesuai budget
Client proxy banyak dan backend tetap penuhSession pinning/multiplexing, transaksi aktif, batas backend seluruh proxyPerbaiki pola session dan pisahkan workload khusus; proxy tidak menambah kapasitas eksekusi database
Timeout memperoleh koneksi tetapi DB masih longgarBatas pool lokal, jaringan, auth/TLS, routing, antrean proxyPerbaiki bottleneck yang terbukti; sesuaikan pool bertahap hanya setelah budget dan load test mendukung
Database CPU/I/O sudah jenuhThroughput, query termahal, cache miss, write contentionKurangi beban, optimalkan query/index; evaluasi scaling database setelah pengukuran

Solusi bila pool/proxy belum mampu menangani beban

  1. 1

    Stabilkan beban: batasi request masuk, concurrency worker, dan batch job; tunda pekerjaan non-kritis. Catat dampak serta kondisi pemulihan.

  2. 2

    Perbaiki connection leak, query lambat, dan transaksi panjang terlebih dahulu. Menambah max_connections atau replica aplikasi tanpa perhitungan dapat memperburuk saturasi.

  3. 3

    Jika proxy tidak tersedia atau tidak kompatibel, gunakan pool driver berbatas kecil, cap total proses/replica, antrean terbatas, dan acquire timeout. Dokumentasikan alasan dan hasil uji; jangan membuat fallback bypass proxy tanpa budget.

  4. 4

    Untuk proses yang boleh ditunda, gunakan queue dengan consumer concurrency terbatas, status tracking, timeout, dan idempotency. Queue menyerap lonjakan, tetapi backlog tetap perlu limit dan monitoring.

  5. 5

    Kurangi read berulang dengan cache yang memiliki TTL/invalidation dan perlindungan cache stampede. Gunakan read replica hanya untuk operasi yang menerima replication lag; write tetap menuju primary.

  6. 6

    Jika kapasitas eksekusi database memang habis, evaluasi CPU/memory/I/O, optimasi schema/index, atau pemisahan workload. Sharding adalah langkah lanjutan jika kebutuhan dan biaya operasionalnya terbukti.

  7. 7

    Ulangi load test dan dokumentasikan budget baru sebelum perubahan pool, proxy, worker, replica, atau database dibawa ke production.

Bukti wajib sebelum production

  • Catat pool min/max per proses, jumlah replica maksimum termasuk rollout, seluruh instance proxy, koneksi langsung, dan alokasi per server database.

  • Pantau active/idle connection, acquire wait p95/p99, pending request, timeout/error, durasi query/transaksi, lock, dan CPU/I/O database. Tetapkan alert dengan PIC serta langkah mitigasi.

  • Uji traffic normal, burst, soak test, worker paralel, rolling deployment, dan failover. Catat target throughput/latency/error sesuai SLO proyek.

  • Simulasikan dependency lambat, query gagal, dan pembatalan request; koneksi harus kembali ke baseline dan antrean harus terkuras setelah beban turun.

  • Dokumentasikan hasil, konfigurasi, dashboard, serta runbook pada dokumentasi database/operasional dan MR. Tanpa bukti batas koneksi dan pemulihan, service belum memenuhi standar readiness.

Referensi eksternalPgBouncer — Pooling Modes & CompatibilityReferensi resmi untuk session/transaction pooling dan kompatibilitas fitur PostgreSQL.https://www.pgbouncer.org/features.htmlBuka referensi Referensi eksternalPgBouncer — Connection LimitsBatas client, pool per user/database, dan koneksi backend; gunakan untuk menghitung budget proxy.https://www.pgbouncer.org/config.htmlBuka referensi Referensi eksternalProxySQL — MultiplexingKondisi yang menahan koneksi backend serta cara memeriksa multiplexing.https://proxysql.com/documentation/multiplexing/Buka referensi Referensi eksternalHikariCP — About Pool SizingMengapa pool yang lebih besar belum tentu meningkatkan throughput dan perlunya pengujian kapasitas.https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-SizingBuka referensi

Aturan khusus SQL dan NoSQL

AreaSQLNoSQL
IdentitasPrimary key wajib stabil; foreign key untuk relasi pentingDocument/key wajib stabil dan mendukung partition strategy
IntegritasGunakan NOT NULL, UNIQUE, CHECK, dan foreign keyValidasi schema di aplikasi dan database jika produknya mendukung
RelasiNormalisasi secukupnya; denormalisasi hanya berdasarkan kebutuhanEmbedding untuk data yang dibaca bersama; reference untuk lifecycle terpisah
AtomicityGunakan transaction untuk perubahan yang harus konsistenPahami batas atomic operation dan transaction pada produk yang dipakai
IndexTinjau execution plan dan biaya writeIndex mengikuti query shape dan partition key
ConsistencyPilih isolation level sesuai risiko bisnisDokumentasikan strong/eventual consistency dan dampaknya ke user

Schema, migration, dan data lifecycle

  • Semua perubahan schema, index, constraint, view, database function, atau reference data wajib melalui migration/versioned script dan code review.

  • Migration wajib masuk ke repository dan merge request yang sama dengan kode yang membutuhkannya agar perubahan dapat ditelusuri serta dijalankan konsisten di setiap environment.

  • Dilarang mengubah struktur database secara manual langsung di production; emergency change wajib segera dibuatkan migration rekonsiliasi dan dokumentasi insiden.

  • Migration production harus kecil, backward-compatible, dapat diobservasi, dan memiliki rollback atau roll-forward plan.

  • Gunakan pola expand-and-contract untuk rename, perubahan tipe, atau penghapusan column/field.

  • Seeder harus idempotent, terpisah antara development dan production, serta tidak membawa credential atau PII.

  • Tetapkan retention, archival, soft/hard delete, dan mekanisme pemenuhan penghapusan data.

  • Backup belum dianggap siap sebelum restore diuji secara berkala dengan target RPO dan RTO yang jelas.

Security dan observability

  • Gunakan credential berbeda per aplikasi dan environment dengan prinsip least privilege.

  • Simpan credential di secret manager atau runtime environment; aktifkan TLS untuk koneksi database.

  • Pantau latency, error rate, slow query, connection usage, lock/deadlock, storage, dan replication lag.

  • Log nama operasi dan durasi tanpa merekam raw credential, token, atau data sensitif.

  • Tetapkan query timeout dan alert berdasarkan service-level objective, bukan nilai default tanpa evaluasi.

  • Audit akses administratif dan perubahan data kritis sesuai kebutuhan bisnis serta regulasi.

Prinsip pemilihan database

Gunakan SQL sebagai pilihan awal ketika data relasional dan transaksi merupakan kebutuhan utama. Gunakan NoSQL ketika access pattern, model data, latency, atau kebutuhan distribusi memang lebih tepat—dan dokumentasikan trade-off consistency, operasional, serta recovery sebelum diadopsi.

08
Execution strategy

Concurrency & Processing Model

Pilih model eksekusi berdasarkan kebutuhan hasil langsung, durasi proses, risiko kegagalan dependency, kapasitas worker, dan pengalaman pengguna.

RuntimeConcurrencyReliability
Concurrency & Processing Model

Gunakan synchronous ketika proses sederhana dan hasilnya dibutuhkan langsung. Gunakan asynchronous ketika pekerjaan lama, berat, dapat ditunda, atau perlu diisolasi dari kegagalan dependency. Jangan memilih async hanya agar arsitektur terlihat modern.

Direct result

Synchronous execution

Proses berjalan langsung dalam request-response dan pengguna menunggu hasil akhirnya.

Deferred result

Asynchronous execution

Pekerjaan dikirim ke background job atau queue sehingga pengguna tidak perlu menunggu selesai.

Overlap

Concurrency

Backend menangani banyak pekerjaan dalam rentang waktu yang saling overlap, tidak harus pada saat yang persis sama.

Simultaneous

Parallelism

Beberapa pekerjaan benar-benar berjalan bersamaan menggunakan beberapa CPU core, process, atau thread.

Event loop

Single-threaded runtime

Satu thread utama tetap dapat concurrent; Node.js melayani banyak operasi I/O melalui event loop.

Worker pool

Multi-threaded runtime

Thread pool dapat menjalankan pekerjaan paralel, terutama untuk CPU-bound atau blocking workload.

Job

Background job & worker

Background worker atau queue worker memproses unit kerja di luar request utama dengan payload, status, timeout, dan retry policy.

Event

Event-driven processing

Producer menerbitkan event dan consumer memprosesnya secara terpisah melalui kontrak berversi.

Sync vs Async

AspekSynchronousAsynchronous
EksekusiSelesai di request yang samaDijadwalkan dan diproses terpisah
Pengalaman userMenunggu hasil finalMenerima acknowledgment dan status pekerjaan
Cocok untukCepat, sederhana, hasil dibutuhkan langsungLama, berat, dapat ditunda, atau dependency tidak stabil
Respons API200/201 dengan hasil final202 Accepted + job_id atau status URL
KegagalanError langsung dikembalikanRetry terbatas, dead-letter queue, dan recovery flow
ObservabilityLatency, throughput, error rateQueue depth, lag, attempts, job age, dead jobs

Concurrency vs Parallelism

AspekConcurrencyParallelism
MaknaBanyak pekerjaan maju secara overlapBanyak pekerjaan berjalan pada saat yang sama
Kebutuhan CPUDapat terjadi pada satu CPU coreUmumnya membutuhkan beberapa core, process, atau thread
Kekuatan utamaEfisiensi menunggu I/OMempercepat pekerjaan CPU-bound
ContohEvent loop menangani banyak koneksiBeberapa worker memproses gambar bersamaan
RisikoStarvation dan blocking event loopRace condition, contention, dan overhead koordinasi

Single-thread vs Multi-thread

AspekSingle-threadMulti-thread
ModelSatu thread utama menjalankan callback/taskBeberapa thread tersedia melalui thread pool/runtime
ConcurrencyTetap bisa concurrent melalui event loop dan non-blocking I/OTask dapat dijadwalkan ke beberapa thread
ParallelismTidak untuk JavaScript CPU work pada thread utamaDapat menjalankan CPU work secara paralel pada beberapa core
Cocok untukI/O-bound dengan operasi non-blockingCPU-bound atau library/runtime blocking
PerhatianBlocking call menghentikan seluruh event loopShared state memerlukan sinkronisasi yang aman

Request-response vs Background job

AspekRequest-responseBackground job
LifecycleTerikat koneksi dan timeout requestDikelola queue worker secara independen
HasilHarus tersedia sebelum responseDapat dilihat melalui polling, callback, atau notifikasi
KapasitasDibatasi web instance dan connectionDibatasi concurrency serta kapasitas worker
Use caseLogin, lookup, validasi, CRUD ringanEmail/WA, report, export, upload/file processing
ScalingTambah web replicaTambah worker berdasarkan queue depth dan downstream limit

Kapan menggunakan queue

  • Gunakan untuk kirim email/WhatsApp, generate report, export data, dan upload atau pemrosesan file.

  • Gunakan ketika proses lama, berat, dapat ditunda, atau bergantung pada layanan eksternal yang rentan gagal.

  • Gunakan ketika throughput perlu di-buffer dan kapasitas worker harus dikontrol terpisah dari web server.

  • Jangan gunakan jika operasi cepat, sederhana, hasilnya dibutuhkan langsung, atau harus atomik dalam transaksi request.

  • Jangan memecah proses ke queue tanpa status tracking dan cara pengguna mengetahui hasil akhirnya.

Async contract

Status tracking

Simpan queued, processing, completed, atau failed beserta progress dan timestamp.

Resilience

Retry mechanism

Pakai exponential backoff + jitter, batas percobaan, dan retry hanya pada error transient.

Boundary

Timeout

Request, network call, job, dan shutdown masing-masing wajib memiliki deadline yang jelas.

Required

Idempotency

Gunakan idempotency key atau unique constraint agar retry tidak menggandakan efek.

Concurrency

Race condition

Lindungi shared state dengan atomic update, optimistic lock, atau lock bila benar-benar perlu.

Backpressure

Worker capacity

Batasi concurrency berdasarkan CPU, memory, koneksi database, rate limit, dan kapasitas downstream.

Observability

Logging & tracing

Teruskan correlation ID dari producer ke message, worker log, dan downstream request.

Event-driven

Event contract

Event-driven flow wajib punya schema version, consumer idempotent, dan prosedur replay.

Implementasi per stack

StackModel eksekusiStandar implementasi
LaravelQueue, Job, dan WorkerGunakan Horizon atau Supervisor; atur retry, timeout, failed jobs, dan idempotency.
Node.js TypeScriptEvent loop, async/await, PromiseJangan block event loop; gunakan BullMQ dengan Redis untuk queue worker bila diperlukan.
GoGoroutine, channel, dan worker poolBatasi goroutine, propagasikan context timeout, tangani panic, dan hindari leak.
Spring BootThread pool, @Async, schedulerKonfigurasi executor secara eksplisit; gunakan message broker jika lifecycle perlu dipisah.
Pythonasync/await atau worker processGunakan async untuk I/O-bound, Celery/RQ untuk job, dan hindari blocking call di async handler.
Definition of done proses async

Setiap background job memiliki status tracking, timeout, retry policy, idempotency, structured logging, correlation ID, batas concurrency worker, dead-letter handling, dan prosedur replay yang terdokumentasi.

Alur event-driven yang aman

  1. 1

    Producer menyimpan perubahan bisnis dan event secara konsisten, misalnya dengan transactional outbox.

  2. 2

    Broker menerima event dengan event ID, correlation ID, schema version, dan occurred_at.

  3. 3

    Queue worker mengambil message sesuai batas concurrency dan menetapkan processing timeout.

  4. 4

    Consumer memeriksa idempotency sebelum menghasilkan side effect.

  5. 5

    Message berhasil di-ack, dijadwalkan retry, atau dipindahkan ke dead-letter queue untuk investigasi.

09
Service boundaries & communication

Microservices

Panduan alur backend lintas service: request-response, event, kepemilikan data, konsistensi, dan pemulihan ketika sebagian proses gagal.

MicroservicesArchitectureAPIEvent
Pisahkan berdasarkan kemampuan bisnis

Service memiliki tanggung jawab bisnis, kontrak, data, dan lifecycle deployment yang jelas. Gunakan microservices ketika kebutuhan deployment, scaling, dan isolasi domain membenarkan biaya operasionalnya. Untuk domain yang belum stabil atau aplikasi sederhana, modular monolith dapat menjadi titik awal.

Contoh pembagian service untuk aplikasi antrian

KomponenTanggung jawabBatas
API GatewayRouting, autentikasi awal, rate limit, dan request IDBusiness rule tetap berada di service domain
Identity ServiceIdentitas, token, dan pengelolaan aksesService tujuan tetap memeriksa izin terhadap resource
Antrian ServicePendaftaran, nomor, kapasitas, dan status antrianPemilik data antrian dan aturan konsistensinya
Notification ServicePengiriman email/WhatsApp dan status pengirimanKegagalan pengiriman tidak membatalkan antrian yang sudah tersimpan
Message brokerMenyalurkan event ke consumerBukan sumber utama status bisnis
Request & event journey

Contoh alur pendaftaran antrian

Satu transaksi pendaftaran, dua jalur pemrosesan.

Synchronous

Daftarkan antrian

  1. Client → API Gateway

    Kirim permintaan pendaftaran beserta token dan idempotency key.

  2. Antrian Service

    Validasi input, izin, dan kapasitas antrian.

  3. Database + Outbox

    Simpan antrian dan event dalam satu transaksi lokal.

Setelah commit, service mengembalikan 201 + antrian_id melalui gateway ke client.

Asynchronous

Kirim notifikasi

  1. Outbox Relay → Broker

    Setelah transaksi commit, relay menerbitkan event AntrianCreated.

  2. Notification Service

    Consumer memeriksa duplikasi event dan memproses pengiriman.

  3. Provider email / WhatsApp

    Kirim pesan, lalu Notification Service menyimpan status pengiriman.

Jika pengiriman gagal, lakukan retry terkontrol. Antrian yang sudah tersimpan tetap valid.

Client dapat mengecek status melalui GET ke Antrian Service lewat gateway. Respons pendaftaran tidak menunggu notifikasi selesai.

Alur synchronous: hasil dibutuhkan langsung

  1. 1

    Client mengirim request melalui gateway dengan token dan idempotency key untuk operasi yang memerlukannya.

  2. 2

    Gateway meneruskan request dan trace context ke service tujuan melalui alamat service yang dikonfigurasi atau service discovery.

  3. 3

    Service memvalidasi input, identitas, izin resource, dan business rule; panggilan downstream memiliki deadline yang mengikuti budget request.

  4. 4

    Service menyimpan hasil dalam transaksi lokal dan mengembalikan response API standar dengan message serta request_id.

  5. 5

    Gunakan 201 saat resource sudah dibuat. Jika pekerjaan baru diterima untuk diproses kemudian, gunakan 202 disertai operation_id dan endpoint status.

Alur asynchronous: tindak lanjut melalui event

  1. 1

    Antrian Service menyimpan perubahan bisnis dan event AntrianCreated pada outbox dalam transaksi database yang sama.

  2. 2

    Relay membaca outbox, menerbitkan event ke broker, lalu mencatat keberhasilan publish. Publish dapat terulang jika relay gagal sebelum mencatat hasil.

  3. 3

    Event membawa event_id, event_type, schema_version, occurred_at, aggregate_id, trace context, dan payload minimum sesuai kontrak.

  4. 4

    Consumer memeriksa event_id dan menjalankan pemrosesan secara idempotent. Setiap service menyimpan status pemrosesannya sendiri.

  5. 5

    ACK dikirim setelah pemrosesan berhasil. Error sementara mendapat retry terbatas dengan backoff dan jitter; kegagalan permanen masuk dead-letter flow untuk investigasi dan replay.

  6. 6

    Untuk side effect eksternal, gunakan idempotency key provider bila tersedia dan rekonsiliasi status ketika hasil pengiriman tidak pasti.

Standar komunikasi dan data

AreaAturan backend
HTTP / gRPCUntuk hasil langsung; hindari rantai panggilan panjang dan tetapkan timeout per dependency
Event / queueUntuk pekerjaan tertunda; dokumentasikan status, duplicate handling, ordering per entity bila diperlukan, dan replay
Data ownershipService lain mengakses melalui API/event, tidak membaca atau menulis tabel milik service lain secara langsung
SQL / NoSQLPilih berdasarkan access pattern; isolation schema dan credential tetap wajib meskipun berbagi instance database
Migration wajibPerubahan schema/index dan transformasi data memakai migration/versioned script pada repository pemilik data; uji kompatibilitas versi lama dan baru
Read modelSalinan data lintas domain diperbarui lewat event; dokumentasikan eventual consistency dan cara rekonsiliasi
ContractVersioning API/event, dokumentasi standar dan Bruno collection diperbarui bersama kode; OpenAPI disinkronkan jika dipakai; uji kompatibilitas producer-consumer
Transaksi lintas service memerlukan pemulihan bisnis

Transaksi database satu service tidak otomatis mencakup service lain. Untuk proses bertahap seperti reservasi kapasitas lalu pembayaran, gunakan Saga bila diperlukan: setiap tahap melakukan transaksi lokal, dan kegagalan memicu tindakan kompensasi seperti melepas reservasi. Kompensasi dapat gagal dan perlu retry serta rekonsiliasi; ini bukan rollback database global. Dokumentasikan orchestrator atau event choreography yang dipilih dan status akhir proses.

Jika sebagian sistem gagal

KondisiPenanganan
Response timeout setelah writeHasil belum tentu gagal; periksa status atau ulangi dengan idempotency key yang sama
Broker tidak tersediaEvent tetap pending di outbox; pantau usia backlog dan pulihkan relay
Dependency terus gagalBatasi retry, gunakan circuit breaker dan isolasi worker/pool; fallback harus menjaga makna hasil bisnis
Event duplikat / terlambatDeduplication dan version check per entity; jangan menimpa state baru dengan event lama
Notification gagalSimpan status failed/pending dan lakukan retry; status antrian tetap mengikuti hasil transaksi Antrian Service
Kompensasi gagalTandai perlu rekonsiliasi, alert ke PIC, dan simpan jejak untuk pemulihan

Dari pengembangan sampai production

  • Hubungkan perubahan service dengan tiket JIRA dan MR; review dampak kontrak ke consumer sebelum merge.

  • Jalankan unit test business rule, integration test database/broker, contract test, dan end-to-end test untuk alur kritis termasuk partial failure.

  • Rilis artifact berversi per service. Urutkan migration dan rollout berdasarkan kompatibilitas; catat service, versi, dan perubahan data di Production Release Record.

  • Amankan komunikasi internal dengan TLS dan identitas service yang tervalidasi; akses internal tetap mengikuti least privilege.

  • Hubungkan logs dan traces lintas HTTP serta message; pantau latency, error, outbox lag, queue age, dan status proses bisnis.

  • Verifikasi healthcheck dan alur bisnis setelah deploy; dokumentasikan rollback aplikasi, roll-forward data, serta replay event yang aman.

Referensi eksternalMicrosoft — Microservices ArchitectureBatas domain, komunikasi service, kepemilikan data, dan trade-off operasional microservices.https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/microservicesBuka referensi Referensi eksternalMicroservices.io — Transactional OutboxPola menyimpan perubahan data dan event secara atomik, beserta risiko publish duplikat.https://microservices.io/patterns/data/transactional-outbox.htmlBuka referensi Referensi eksternalMicroservices.io — SagaTransaksi lokal berurutan dan kompensasi untuk proses bisnis lintas service.https://microservices.io/patterns/data/saga.htmlBuka referensi
10
Logs, metrics & traces

Observability

Standar telemetry backend agar tim dapat mendeteksi gangguan, memahami dampaknya, menelusuri penyebab, dan memverifikasi pemulihan dengan cepat.

LogsMetricsTracingSRE
Observability bukan hanya logging

Monitoring memberi tahu ketika kondisi yang telah dikenali bermasalah. Observability membantu menjawab mengapa masalah terjadi dengan menghubungkan logs, metrics, dan traces pada service, environment, version, request, serta operasi bisnis yang sama.

Event detail

Logs

Rekaman event terstruktur untuk menjelaskan apa yang terjadi, kapan, pada service mana, dan dalam konteks request atau job apa.

System health

Metrics

Pengukuran numerik teragregasi untuk melihat kesehatan, tren, kapasitas, serta pencapaian SLI/SLO.

Request flow

Traces

Perjalanan end-to-end sebuah request atau job melalui service, database, queue, dan external dependency.

Shared context

Correlation

request_id, trace_id, dan span_id menghubungkan telemetry agar investigasi tidak dilakukan dengan menebak waktu kejadian.

Visibility

Dashboards

Ringkasan operasional untuk latency, traffic, error, saturation, dependency, queue, database, dan indikator bisnis kritis.

Response

Alerts

Notifikasi actionable berbasis dampak pengguna atau risiko SLO, lengkap dengan owner, severity, dan runbook.

Structured logging standard

FieldAturan
timestampGunakan waktu UTC dalam format ISO 8601.
levelGunakan debug, info, warn, dan error secara konsisten; production tidak menjalankan debug tanpa kebutuhan terbatas.
service & environmentCatat nama service, environment, version, serta instance/pod bila tersedia.
message & eventGunakan pesan singkat yang stabil dan event name yang dapat dicari, bukan paragraf bebas.
request_id & trace_idTerima atau buat ID di entry point, lalu teruskan ke downstream request, event, dan background job.
operationCatat route/template, method, status, duration_ms, serta error_code; jangan jadikan raw URL ber-ID sebagai label metric.
business contextBoleh mencatat entity ID atau operation ID yang aman dan diperlukan untuk investigasi.
errorCatat jenis error dan stack trace pada boundary terpusat tanpa menduplikasi error yang sama di setiap layer.
Contoh structured log
{
  "timestamp": "2026-09-06T09:21:42.117Z",
  "level": "error",
  "service": "prima-api",
  "environment": "production",
  "version": "v1.8.0",
  "event": "queue.job_failed",
  "message": "Gagal memproses sinkronisasi antrian",
  "request_id": "req_01K4...",
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "job_id": "job_01K4...",
  "attempt": 3,
  "duration_ms": 1820,
  "error_code": "DOWNSTREAM_TIMEOUT"
}

Metric minimum backend

AreaMetric yang dipantauTujuan
TrafficRequest rate dan throughput per route/serviceMelihat beban serta perubahan pola penggunaan.
ErrorsError rate, status 5xx/4xx penting, exception, dan failed jobMendeteksi kegagalan teknis dan bisnis.
Latencyp50, p95, dan p99 response/dependency/job durationMenangkap pengalaman lambat yang tertutup oleh nilai rata-rata.
SaturationCPU, memory, thread/goroutine, connection pool, disk, dan worker utilizationMendeteksi kapasitas yang mendekati batas.
DatabaseQuery latency, slow query, connection usage, lock/deadlock, replication lagMengetahui bottleneck dan risiko data layer.
QueueQueue depth, oldest job age, processing rate, retry, failed/dead-letter jobMengukur backlog dan kesehatan worker.
DependencyLatency, error, timeout, circuit-breaker state, dan rate-limit responseMembedakan kegagalan internal dengan downstream.
BusinessKeberhasilan proses kritis seperti login, pendaftaran, pembayaran, atau penerbitan dokumenMengukur apakah service benar-benar memberi hasil kepada pengguna.

Distributed tracing dan context propagation

  • Buat root span pada HTTP entry point, consumer event, scheduler, dan queue worker.

  • Buat child span untuk database query, cache, external HTTP call, publish message, dan operasi penting lain.

  • Teruskan trace context menggunakan standar W3C Trace Context melalui header dan message metadata.

  • Sertakan trace_id dan span_id pada structured log agar perpindahan dari alert ke trace dan log mudah dilakukan.

  • Jangan menaruh request body, response body, token, atau PII sebagai span attribute tanpa klasifikasi dan sanitasi.

  • Gunakan sampling yang terdokumentasi; pertahankan trace error dan operasi kritis sesuai kemampuan platform.

Healthcheck, dashboard, dan alert

KomponenStandar
LivenessMenjawab apakah proses hidup; jangan gagal hanya karena dependency sementara tidak tersedia.
ReadinessMenjawab apakah instance siap menerima traffic dan memeriksa dependency yang benar-benar wajib.
DashboardTampilkan golden signals, deployment marker/version, dependency, database, queue, serta business flow kritis.
AlertAlert berdasarkan gejala dan dampak yang perlu tindakan; hindari alert untuk setiap exception tunggal.
SeverityDefinisikan severity, jalur eskalasi, owner, jam dukungan, dan target response.
RunbookSetiap alert penting memiliki link dashboard, query awal, kemungkinan penyebab, mitigasi, dan prosedur eskalasi.

SLI, SLO, dan alerting

IstilahPenggunaan
SLIUkuran perilaku service dari sudut pandang pengguna, misalnya successful request ratio atau latency p95.
SLOTarget reliabilitas untuk SLI pada periode tertentu, disepakati sesuai kebutuhan bisnis.
Error budgetBatas toleransi kegagalan yang membantu menyeimbangkan reliability dengan kecepatan perubahan.
Alert thresholdGunakan threshold yang terkait dampak/SLO dan durasi yang cukup agar tidak menghasilkan noise.

Keamanan dan kualitas telemetry

  • Dilarang mencatat password, access token, refresh token, API key, session, cookie, atau authorization header.

  • Masking atau hapus PII dan data sensitif sebelum telemetry meninggalkan aplikasi.

  • Batasi metric label dan span attribute ber-cardinality tinggi seperti user ID, raw URL, atau payload dinamis.

  • Tetapkan retention, akses, enkripsi, dan audit untuk platform observability sesuai klasifikasi data.

  • Telemetry tidak boleh menyebabkan request gagal; gunakan buffering, timeout, dan batas resource pada exporter/agent.

  • Pantau pipeline telemetry itu sendiri: dropped data, exporter failure, collector queue, dan ingestion delay.

Implementasi per stack

StackStandar implementasi
LaravelGunakan structured logging dengan context, request ID middleware, Horizon metrics untuk queue, dan OpenTelemetry SDK/exporter bila tracing diterapkan.
Node.js TypeScriptGunakan logger JSON seperti Pino/Winston, AsyncLocalStorage untuk request context, serta OpenTelemetry Node SDK dan auto-instrumentation yang relevan.
GoGunakan log/slog atau logger terstruktur, context untuk propagasi, OpenTelemetry Go SDK, serta Prometheus-compatible metrics bila menjadi standar platform.
Spring BootGunakan Actuator/Micrometer untuk health dan metrics, MDC untuk correlation, structured logging, serta OpenTelemetry Java agent/SDK.
PythonGunakan logging terstruktur, contextvars untuk correlation, OpenTelemetry Python, dan instrumentasi ASGI/WSGI, database, serta worker.
Referensi eksternalOpenTelemetry Observability PrimerReferensi vendor-neutral untuk konsep telemetry, logs, metrics, traces, reliability, SLI, dan SLO.https://opentelemetry.io/docs/concepts/observability-primer/Buka referensi Referensi eksternalGoogle SRE — Monitoring Distributed SystemsReferensi untuk golden signals, dashboard yang berguna, serta alert yang actionable dan rendah noise.https://sre.google/sre-book/monitoring-distributed-systems/Buka referensi
Definition of done observability

Service mengeluarkan logs, metrics, dan traces yang saling terhubung; dashboard memperlihatkan kesehatan teknis serta flow bisnis; alert memiliki owner dan runbook; deployment dapat dibandingkan melalui version marker; dan seluruh telemetry bebas secret serta data sensitif yang tidak diperlukan.

11
Container baseline

Docker Standard

Image production harus kecil, deterministic, aman, tidak membawa secret, dan menjalankan proses sebagai non-root.

DockerSecurityProduction
Wajib

Dockerfile

Multi-stage build, base image dipin, cache dependency optimal, dan runtime minimal.

Wajib

.dockerignore

Abaikan .git, env, test artifacts, local dependency, logs, dan secret.

Local

Compose

Gunakan untuk dependency local seperti database, Redis, atau broker bila diperlukan.

Wajib

.env.example

Dokumentasikan key tanpa nilai rahasia dan jelaskan apakah buildtime/runtime.

Runtime

Healthcheck

Pisahkan liveness dan readiness; readiness memeriksa kesiapan menerima traffic.

Security

Non-root

Buat user khusus atau gunakan user bawaan image sebelum proses dijalankan.

Contoh Dockerfile production
FROM node:24-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM node:24-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:24-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=deps --chown=node:node /app/node_modules ./node_modules
USER node
HEALTHCHECK --interval=30s CMD wget -qO- http://localhost:3000/health || exit 1
EXPOSE 3000
CMD ["node", "dist/server.js"]
Definition of done

Image dibangun di CI, hasil scan tidak memiliki vulnerability kritis, berjalan sebagai non-root, menerima config saat runtime, dan lulus smoke test dari image yang sama dengan production.

12
Environment strategy

Buildtime vs Runtime

Pisahkan nilai untuk menghasilkan artifact dari konfigurasi yang baru diketahui ketika aplikasi dijalankan.

DockerRuntimeSecurity

Perbandingan environment

AspekBuildtimeRuntime
TujuanMenghasilkan artifact/imageMengonfigurasi instance berjalan
ContohInstall deps, compile TS, binary Go, JARDATABASE_URL, REDIS_URL, LOG_LEVEL
Waktu tersediaCI atau docker buildContainer start / platform config
SecretHindari; gunakan secret mount sementaraInject via secret manager/platform
PerubahanMemerlukan rebuildRestart/redeploy tanpa rebuild artifact
PrinsipArtifact immutable dan reproducibleSatu artifact untuk semua environment
.env.example
# Runtime — tidak berisi nilai asli
APP_ENV=
LOG_LEVEL=
DATABASE_URL=
REDIS_URL=
API_KEY=
QUEUE_CONNECTION=

# Build metadata — aman untuk diekspos
APP_VERSION=
COMMIT_SHA=

Aturan keamanan

  • Jangan gunakan ARG atau ENV Docker untuk menyimpan secret production permanen.

  • Validasi seluruh runtime variable saat startup dan hentikan proses bila key wajib kosong.

  • Rotasi secret tanpa mengubah source code dan batasi akses berdasarkan environment.

  • Jangan log isi environment atau credential ketika validasi gagal.

13
PHP standard

Laravel

Gunakan kemampuan framework untuk boundary HTTP, tetapi pertahankan business rule di service yang dapat diuji.

StackPHP
AreaStandar LaravelTooling
HTTPController tipis, Form Request, API ResourceOpenAPI / Scramble
BusinessService/Action berisi use caseDependency injection
DataRepository bila abstraksi memberi nilaiEloquent + eager loading
QualityPSR-12, static analysis level tinggiPint, PHPStan/Larastan
TestFeature test untuk API, unit untuk domainPest atau PHPUnit
AsyncJob idempotent, retry/backoff, failed jobsQueue + Horizon
SchemaMigration kecil dan backward-compatibleArtisan migrate
Envconfig() di aplikasi; jangan env() di luar config.env.example
RuntimeGunakan FrankenPHP; aktifkan worker mode hanya setelah state dan memory lifecycle diujiFrankenPHP / Octane
Struktur folder
app/
├── Actions/
├── Http/Controllers/Api/V1/
├── Http/Requests/
├── Http/Resources/
├── Models/
├── Repositories/
├── Services/
└── Transformers/
tests/
├── Feature/
└── Unit/
Transformers

Gunakan app/Transformers/ untuk memetakan model atau hasil service ke struktur data response yang konsisten. Batasi tanggung jawabnya pada pemetaan field dan format data; business logic tetap di service dan query di repository. Jika dipakai bersama Http/Resources, tetapkan Transformers sebagai mapper dan Resources sebagai pembungkus response agar pemetaan tidak diduplikasi.

Dockerfile Laravel + FrankenPHP
FROM dunglas/frankenphp:1-php8.4-bookworm AS base

RUN install-php-extensions \
    pdo_mysql \
    pdo_pgsql \
    intl \
    opcache \
    zip

WORKDIR /app

FROM base AS build
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
RUN composer install \
    --no-dev \
    --no-interaction \
    --no-scripts \
    --prefer-dist \
    --optimize-autoloader
COPY . .
RUN composer dump-autoload --no-dev --classmap-authoritative --no-scripts \
    && php artisan package:discover --ansi

FROM base AS runtime
ENV APP_ENV=production \
    APP_DEBUG=false \
    SERVER_NAME=:8080 \
    XDG_CONFIG_HOME=/config \
    XDG_DATA_HOME=/data

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
    && mkdir -p /config /data storage/framework/cache storage/framework/sessions storage/framework/views \
    && chown -R www-data:www-data /config /data /app

COPY --from=build --chown=www-data:www-data /app /app

USER www-data
EXPOSE 8080

CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile"]
14
JavaScript runtime

Node.js TypeScript

Strict TypeScript, validasi runtime, dan singleton resource adalah baseline layanan Node.js yang sehat.

StackTypeScript
AreaStandar Node.jsTooling
HTTPRoute/controller hanya orkestrasiFastify / NestJS / Express
ValidationDTO divalidasi saat runtimeZod / Valibot / class-validator
BusinessService bebas detail transportDependency injection
DataRepository + pool/client singletonPrisma / TypeORM / Knex
Qualitystrict: true, no implicit anyESLint + Prettier
TestUnit, integration, contractVitest atau Jest
AsyncWorker terpisah dan idempotentBullMQ / broker client
EnvSchema env divalidasi ketika startupZod + process.env
RuntimeJalankan aplikasi melalui pm2-runtime; PM2 wajib berada di production dependenciesPM2
Struktur folder
src/
├── config/
├── modules/users/
│   ├── user.controller.ts
│   ├── user.schema.ts
│   ├── user.service.ts
│   └── user.repository.ts
├── shared/
├── workers/
└── server.ts
Dockerfile Node.js + PM2
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci \
    && npm ls pm2 --omit=dev
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:24-alpine AS runtime
ENV NODE_ENV=production \
    PM2_HOME=/tmp/pm2
WORKDIR /app
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/node_modules ./node_modules
RUN mkdir -p /tmp/pm2 && chown -R node:node /tmp/pm2
USER node
EXPOSE 3000
CMD ["./node_modules/.bin/pm2-runtime", "--json", "dist/server.js", "--name", "api"]
15
Compiled service

Go

Gunakan context untuk deadline, interface pada consumer boundary, dan graceful shutdown pada semua server.

StackGo
AreaStandar GoTooling
HTTPHandler parse/validate/map responsenet/http / Chi / Gin
BusinessUsecase menerima contextInterface kecil
DataRepository; sql.DB sebagai singleton pooldatabase/sql / sqlc
TimeoutDeadline diteruskan ke downstreamcontext.WithTimeout
QualityFormat dan static analysis wajibgofmt, go vet, golangci-lint
TestTable-driven + race detectorgo test -race ./...
ShutdownStop traffic, drain request, close poolsignal.NotifyContext
EnvParse dan validate config satu kalienvconfig / custom
Struktur folder
cmd/api/main.go
internal/
├── config/
├── user/
│   ├── handler.go
│   ├── service.go
│   └── repository.go
├── platform/database/
└── worker/
migrations/
Dockerfile Go
FROM golang:1.25-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/api ./cmd/api

FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /bin/api /api
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/api"]
16
Java platform

Spring Boot

Layer yang jelas, DTO tervalidasi, exception terpusat, dan HikariCP terukur menjadi baseline layanan Java.

StackJava
AreaStandar Spring BootTooling
HTTPController + DTO, tidak expose entityJakarta Validation
ErrorSatu format error global@RestControllerAdvice
BusinessService dengan transaction boundary@Transactional
DataRepository dan query terukurSpring Data JPA
PoolUkur size, timeout, lifetimeHikariCP + metrics
SchemaMigration versioned di CI/deployFlyway / Liquibase
TestUnit dan sliced integration testJUnit 5 / Mockito / Testcontainers
EnvTyped configuration + validation@ConfigurationProperties
Struktur package
com.company.service/
├── config/
├── common/exception/
└── user/
    ├── UserController.java
    ├── UserService.java
    ├── UserRepository.java
    ├── dto/
    └── entity/
Dockerfile Spring Boot
FROM eclipse-temurin:25-jdk-alpine AS build
WORKDIR /app
COPY .mvn .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src src
RUN ./mvnw package -DskipTests

FROM eclipse-temurin:25-jre-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
17
Python services

Python

Tegaskan lifecycle session, schema boundary, dan proses worker agar layanan Python stabil dalam jangka panjang.

StackPython
AreaStandar PythonTooling
FrameworkPilih sesuai kebutuhan, bukan trenFastAPI / Django / Flask
ValidationSchema request dan response eksplisitPydantic / DRF Serializer
BusinessService terpisah dari route/viewDependency injection ringan
DataSession per unit-of-work, selalu closeSQLAlchemy / Django ORM
QualityType hint dan format konsistenRuff + mypy/pyright
TestFixture terisolasi dan integration DBpytest + Testcontainers
AsyncWorker terpisah, retry idempotentCelery / RQ
EnvTyped settings divalidasi startuppydantic-settings
Struktur folder
app/
├── api/v1/
├── core/config.py
├── db/session.py
├── models/
├── schemas/
├── repositories/
├── services/
├── workers/
└── main.py
tests/
Dockerfile Python
FROM python:3.14-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt

FROM python:3.14-slim
RUN useradd --create-home app
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/*
COPY --chown=app:app . .
USER app
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
18
Documentation baseline

Dokumentasi

Dua keluaran mandatory untuk setiap repository backend: dokumentasi standar service dan Bruno API collection. Dokumen tambahan disediakan sesuai kebutuhan.

DokumentasiMandatoryBrunoOpsional
Mandatory

Dokumentasi standar service

Gunakan docs/README.md sebagai indeks. Kelompokkan scope, alur bisnis, API, database, arsitektur, dan operasional dalam folder 01 sampai 06.

Mandatory

Bruno API collection

Simpan seluruh endpoint aktual dalam docs/03-api/bruno/ di repository: auth, environment non-rahasia, variable, contoh request/response, dan assertion penting.

Struktur dokumentasi backend

Klik nama file untuk membuka contoh isinya. File Markdown, OpenAPI, dan Bruno tersedia.

docs/
├── 01-overview/
│   ├── system-context.md
│   └── container-diagram.md
├── 02-business-flow/
│   ├── application-flow.md
│   └── business-rules.md
├── 03-api/
│   ├── openapi.yaml
│   ├── authentication.md
│   ├── error-codes.md
│   └── bruno/
│       ├── bruno.json
│       ├── environments/
│       │   ├── local.bru
│       │   ├── staging.bru
│       │   └── production.bru.example
│       ├── auth/
│       │   └── login.bru
│       └── antrian/
│           ├── create-antrian.bru
│           └── detail-antrian.bru
├── 04-database/
│   ├── erd.md
│   └── data-dictionary.md
├── 05-architecture/
│   ├── sequence-diagrams.md
│   └── adr/
│       └── 0001-notifikasi-asynchronous.md
├── 06-operations/
│   ├── deployment.md
│   ├── environment.md
│   ├── monitoring.md
│   └── incident-runbook.md
└── README.md
Contoh: sistem pendaftaran antrian

Folder docs/ berisi template dengan contoh, bukan API yang sudah tersedia di portal. Sesuaikan service, endpoint, data, dan prosedur operasional dengan proyek. Diagram, OpenAPI, ADR, dan runbook terpisah tetap opsional; dokumentasi standar dan Bruno collection mandatory.

Contoh isi setiap kelompok

FolderContoh isi
01-overviewPengguna, gateway, Antrian Service, database, dan provider notifikasi
02-business-flowLogin → daftar → simpan antrian → notifikasi; rule kapasitas dan idempotency
03-apiOpenAPI, autentikasi Bearer, error codes, dan Bruno: login/create/detail
04-databaseERD serta dictionary service, antrian, idempotency, dan outbox
05-architectureSequence diagram dan contoh ADR pengiriman notifikasi asynchronous
06-operationsTemplate deployment, environment, monitoring, dan penanganan notifikasi tertunda
Contoh docs/03-api/bruno/antrian/create-antrian.bru
meta {
  name: Create Antrian
  type: http
  seq: 1
}

post {
  url: {{base_url}}/api/v1/antrian
  body: json
  auth: bearer
}

auth:bearer {
  token: {{access_token}}
}

headers {
  Content-Type: application/json
  Accept: application/json
  Idempotency-Key: {{idempotency_key}}
}

body:json {
  {
    "service_code": "GENERAL"
  }
}

script:post-response {
  bru.setVar("antrian_id", "");
  if (res.getStatus() === 201) {
    bru.setVar("antrian_id", res.getBody().data.antrian_id);
  }
}

tests {
  test("Antrian dibuat dengan status waiting", function () {
    const body = res.getBody();
    expect(res.getStatus()).to.equal(201);
    expect(body.success).to.equal(true);
    expect(body.message).to.be.a("string");
    expect(body.data.antrian_id).to.be.a("string").and.not.empty;
    expect(body.data.status).to.equal("waiting");
    expect(body.meta.request_id).to.be.a("string");
  });
}

docs {
  Contoh request yang mengubah data. Jalankan Login lebih dahulu.
  Gunakan key baru untuk pendaftaran baru; retry memakai key dan payload yang sama.
}

Mencoba contoh Bruno

  1. 1

    Buka docs/03-api/bruno/ di Bruno dan pilih environment local; base_url contoh http://localhost:8080.

  2. 2

    Siapkan backend sesuai OpenAPI contoh atau sesuaikan request dengan endpoint proyek. Portal ini tidak menyediakan endpoint tersebut.

  3. 3

    Sediakan LOGIN_EMAIL dan LOGIN_PASSWORD melalui environment proses Bruno lokal, lalu jalankan Login untuk memperoleh access_token sementara.

  4. 4

    Jalankan Create Antrian, lalu Detail Antrian; antrian_id diteruskan melalui runtime variable.

  5. 5

    Periksa assertion. Ganti idempotency_key untuk operasi baru; retry tetap menggunakan key dan body yang sama.

Isi mandatory dokumentasi standar

  • Identitas service, tujuan, scope bisnis, PIC teknis, repository, dan link JIRA.

  • Versi runtime, dependency, instalasi, command start, dan konfigurasi melalui .env.example tanpa secret.

  • Struktur kode, alur bisnis utama, serta dependency antar-service.

  • Database ownership, lokasi/command migration wajib, urutan eksekusi, dan recovery plan.

  • Lokasi docs/03-api/bruno/, cara memilih environment, autentikasi, dan menjalankan request.

  • Command test/build, deployment, healthcheck, observability, dan tautan release record di JIRA/MR.

Isi mandatory Bruno collection

  • Setiap endpoint memiliki request dengan method, path, parameter, header, body, dan kebutuhan autentikasi.

  • Group berdasarkan module/domain; gunakan nama request yang jelas dan file kebab-case.

  • Gunakan base_url dan variable token sesuai kebutuhan; simpan hanya konfigurasi non-rahasia di Git.

  • Dokumentasikan response/message dan error; tambahkan assertion untuk status dan hasil bisnis penting.

  • Perbarui collection bersama perubahan endpoint dan sertakan hasil pengujian pada MR.

Dokumen tambahan — opsional

DokumenKapan berguna
ADRKeputusan arsitektur memiliki trade-off yang perlu dilacak
Diagram arsitektur / sequenceAlur antar-service sulit dijelaskan dengan teks
OpenAPI / Swagger terpisahConsumer memerlukan schema machine-readable atau generated client
Runbook / troubleshooting terpisahProsedur operasional membutuhkan panduan rinci
Changelog terpisahPerlu ringkasan release selain catatan JIRA/MR
Onboarding terpisahSetup membutuhkan petunjuk lebih panjang
Opsional sebagai dokumen terpisah

Informasi penting tentang API, migration, deployment, dan recovery tetap wajib tersedia dalam dua keluaran baseline atau tautan JIRA/MR terkait. Dokumen tambahan tidak diwajibkan sebagai file tersendiri; jika dipakai, tetap perbarui bersama perubahan kode.

Update bersama merge request

Developer memperbarui dokumentasi dan collection dalam MR yang sama. Reviewer memastikan isinya sesuai implementasi, dapat digunakan untuk setup dan verifikasi, serta bebas secret. Indeks dan contoh lengkap tersedia di docs/README.md. Bruno berada di docs/03-api/bruno/. Contoh memakai sistem antrian dan harus disesuaikan dengan backend proyek.

19
Before merge

Backend Self-Check

Pemeriksaan mandiri teknis sebelum push, merge, atau meminta review dari engineer lain.

ChecklistQuality
Gunakan sebagai quality gate

Checklist tersimpan pada browser ini. Centang hanya setelah Anda memverifikasi implementasi atau bukti CI, bukan berdasarkan asumsi.

Progress pemeriksaan0 dari 20 selesai
20
Release readiness

Production Checklist

Syarat minimum sebelum sebuah layanan menerima traffic production dan masuk rotasi operasional.

ChecklistProductionSecurity
Setiap deployment harus punya release record

Catat apa yang dirilis, perubahan database, cara deployment, bukti verifikasi, serta rollback plan sebelum meminta approval production. Salin hasil form ke tiket JIRA atau merge request agar menjadi dokumentasi bersama dan dapat ditelusuri kembali.

Alur deployment backend ke production

  1. 1

    Pastikan merge request telah disetujui, pipeline lulus, artifact/image sudah teridentifikasi, dan scope release sesuai tiket JIRA.

  2. 2

    Lengkapi Production Release Record, termasuk daftar fitur, perubahan API/config/dependency, serta dampak ke consumer.

  3. 3

    Jika ada migration, review script, durasi, lock risk, kompatibilitas dengan versi aplikasi lama/baru, backup, dan rollback atau roll-forward plan.

  4. 4

    Dapatkan approval dari pihak yang berwenang dan tentukan PIC, waktu deployment, kanal komunikasi, serta kondisi go/no-go.

  5. 5

    Jalankan migration dan deployment mengikuti urutan yang telah disetujui; jangan menjalankan perubahan manual yang tidak tercatat.

  6. 6

    Lakukan smoke test, healthcheck, validasi endpoint/queue/database, serta pantau log, metric, error rate, dan latency.

  7. 7

    Jika acceptance criteria gagal atau metrik memburuk melewati batas, hentikan rollout dan jalankan rollback/roll-forward plan.

  8. 8

    Perbarui release record dan tiket JIRA dengan hasil akhir, waktu selesai, versi yang aktif, serta incident/follow-up bila ada.

Standar perubahan database

AreaYang wajib didokumentasikan
SchemaTabel/collection, column/field, index, constraint, tipe data, dan relasi yang ditambah, diubah, atau dihapus.
Data migrationData yang dipindahkan/backfill, estimasi volume dan durasi, batching, serta cara melanjutkan bila proses terhenti.
CompatibilityPastikan migration kompatibel dengan aplikasi sebelum dan sesudah deploy; gunakan pola expand-migrate-contract untuk breaking change.
Operational riskPotensi lock, downtime, kenaikan CPU/storage, replication lag, dan dampak terhadap query atau service lain.
RecoveryBackup/PITR yang tersedia, rollback atau roll-forward script, PIC eksekusi, dan batas waktu keputusan.
VerificationQuery atau pemeriksaan untuk memastikan schema, jumlah data, constraint, index, serta aplikasi berjalan benar.

Guardrail deployment di GitLab

KontrolStandar
Protected environmentBatasi akses deploy production hanya untuk role atau pengguna yang berwenang.
Manual approvalGunakan approval sebelum job production dijalankan jika fitur tersedia pada GitLab BIT.
Single deploymentCegah dua deployment berjalan bersamaan pada environment yang sama, misalnya dengan resource_group: production.
Protected variableSimpan secret sebagai protected dan environment-scoped CI/CD variable; jangan tulis nilainya di form atau repository.
TraceabilityCatat environment, commit SHA/image tag, pipeline, pelaksana, waktu, dan hasil deployment.
Dokumentasi deployment

Production Release Record

Setelah lengkap, salin Markdown atau unduh dokumen untuk dilampirkan ke JIRA atau merge request.

Form internal
Identitas release
Ruang lingkup perubahan
Database migration
Test dan verifikasi
Jangan masukkan secret. Dokumentasikan nama variable atau lokasi secret manager, bukan password, token, API key, atau credential production.
Referensi eksternalGitLab Deployment SafetyReferensi resmi untuk protected environment, deployment approval, pencegahan concurrent deployment, dan perlindungan variable production.https://docs.gitlab.com/ci/environments/deployment_safety/Buka referensi
Progress pemeriksaan0 dari 17 selesai
Ready berarti dapat dioperasikan

Deployment sukses bukan akhir pekerjaan. Layanan dinyatakan siap ketika tim dapat mendeteksi masalah, mendiagnosisnya melalui telemetry, dan melakukan rollback dengan aman.