Skip to content
Architecture Programming

Single-File Tool Architecture

By Romi Fadli, S.Kom. 8 min read Software Design
On this page
A single file card holding six sections in a fixed order — Config, Helpers, Reporter, Steps, Summary, Entrypoint — with arguments, a config file and a prompt flowing in from the left and exit codes 0, 1, 2 and 130 flowing out to the right

Single-File Tool Architecture adalah bentuk yang saya berikan pada tool kecil: satu file yang mengerjakan satu tugas, dengan seksi-seksi yang urutannya selalu sama dan kontrak tetap tentang cara menjalankannya serta cara ia melaporkan hasil. Ini kesepakatan saya sendiri, bukan rancangan dari buku. Bentuknya lahir dari menulis banyak tool kecil yang memeriksa server, membuat deployment stack, dan mengonversi file.

Ini salah satu arsitektur yang saya pakai, dan ia mengisi ujung terkecil pekerjaan saya. Untuk service yang memegang aturan bisnis sungguhan dan hidup bertahun-tahun, saya memakai yang lebih berat; untuk tool yang mengerjakan satu hal lalu selesai, bentuk ini sudah cukup.

Sekilas

AsalKesepakatan saya sendiri, terbentuk dari banyak tool command-line kecil
Saya pakai untukTool dengan satu tugas jelas yang dijalankan orang atau script lain: pemeriksa kesiapan, generator stack, konverter
Tak saya pakai untukService berumur panjang dengan banyak use case
BahasaPython, Bash
Posisi Single-File Tool Architecture di antara arsitektur yang saya pakai.

Gagasannya secara singkat

A. Satu file, satu tugas

Seluruh tool tinggal di satu script. Di sebelahnya cuma ada yang dibacanya: daftar dependency, contoh file konfigurasi, dan untuk generator, folder berisi template. Tak ada package, tak ada impor antarfile dalam satu tool, dan tak ada yang perlu dipasang selain dependency-nya.

B. Dibaca dari atas ke bawah

File-nya dibagi menjadi seksi yang ditandai komentar banner, dan urutan seksinya selalu sama: konfigurasi, helper, reporter, langkah-langkah yang mengerjakan tugasnya, ringkasan, lalu entry point. Membaca file dari atas ke bawah sama dengan mengikuti perjalanan satu kali jalan.

C. Kontrak di bagian atas

Baris-baris pertama file menyatakan apa yang dikerjakan tool itu dan apa arti exit code-nya. Exit code adalah angka yang dikembalikan program saat berakhir, dan dari situlah script lain membedakan sukses dari gagal tanpa membaca output.

D. Tiga pintu masuk

Tool menerima input lewat paling banyak tiga cara: argumen command-line untuk opsi, file konfigurasi untuk semua yang menggambarkan satu target, dan prompt interaktif untuk nilai yang berubah tiap kali jalan. Tiap jenis nilai punya satu pintu masuk, jadi satu kali jalan mudah diulang.

Kenapa saya memilihnya

Lapisan membuat hal kecil jadi rumit. Tool dengan satu tugas tak butuh domain, use case, dan adapter. Memecah beberapa ratus baris ke beberapa folder akan membuat strukturnya lebih besar daripada programnya, dan saya akan lebih banyak bernavigasi ketimbang membaca.

Satu file mudah disalin dan dipindah. Saya bisa membawa tool ke mesin lain sebagai satu script, konfigurasinya, dan daftar dependency-nya. Tak ada package yang harus di-build atau diterbitkan dulu supaya ia bisa jalan di tempat lain.

Urutan yang sama di tiap tool memberi tahu saya letak sesuatu. Karena tiap tool menjaga urutan seksinya tetap sama, saya tahu di mana konfigurasi divalidasi atau di mana ringkasan dicetak pada tool yang berbulan-bulan tak saya buka, sebelum membaca satu baris pun.

Script lain bisa memanggilnya. Exit code yang tetap, dan input yang bisa datang dari argumen atau environment, membuat tool yang hari ini saya jalankan dengan tangan bisa dijalankan scheduler atau pipeline besok tanpa diubah.

Kesepakatan saya

Tool saya yang menyatakan mengikuti arsitektur ini punya bentuk seperti berikut. Di Python:

python
#!/usr/bin/env python3
"""What the tool checks or does.

Exit code:
    0 - every check passed
    1 - a check failed
    2 - the configuration is missing or invalid
  130 - cancelled by the user
"""

# --- Config ------------------------------------------------------------
# --- Helpers -----------------------------------------------------------
# --- Reporter ----------------------------------------------------------
# --- Steps -------------------------------------------------------------
# --- Summary -----------------------------------------------------------
# --- Entrypoint --------------------------------------------------------
SeksiIsinya
Configmemuat file konfigurasi dan memvalidasi tiap field dengan ketat, sebelum koneksi apa pun dibuka
Helpersfungsi kecil yang dipakai bersama oleh langkah-langkahnya, misalnya mengubah error menjadi satu baris yang mudah dibaca
Reportermencetak satu baris per check, lolos atau gagal, dan mengumpulkan fakta untuk ringkasan
Stepspekerjaan yang sesungguhnya, satu seksi per bagian tugas
Summaryjumlah check yang lolos dan status akhir
Entrypointmengurai argumen, menjalankan langkah-langkahnya, dan memilih exit code
Urutan tetap seksi-seksi di dalam tool satu file.

Tiap jenis input punya pintu masuknya sendiri:

InputDipakai untukContoh
argumen command-lineopsi yang mengubah cara tool berjalan--timeout 30
file konfigurasisemua yang menggambarkan satu target, satu file per target--config server-a.json
prompt interaktifnilai yang berubah tiap kali jalanalamat tujuan pesan uji
Jenis nilai mana masuk ke tool lewat pintu yang mana.

Dependency pihak ketiga yang belum terpasang juga mengakhiri jalan dengan exit code 2 dan pesan tentang cara memasangnya. Di sebelah tiap tool ada contoh file konfigurasi berisi placeholder, dan file konfigurasi sungguhan, yang memuat kredensial, tak pernah di-commit.

Varian Bash dipakai untuk generator — script yang membuat folder siap pakai dari template. Header-nya mendaftar environment variable yang dihormatinya, dan tiap input dibaca dengan cara yang sama: dari environment bila sudah di-set, kalau belum, lewat pertanyaan dengan nilai default.

bash
# ask VAR "Prompt" "default": pakai environment variable bila ada, kalau tidak, tanyakan
ask() {
    local var="$1" prompt="$2" default="$3"
    [[ -n "${!var:-}" ]] && return
    read -r -p "$prompt [$default]: " input || true
    printf -v "$var" '%s' "${input:-$default}"
}

Satu aturan itu membuat generator yang sama bisa jalan interaktif untuk saya dan tanpa penunggu di automation, dengan satu variable tambahan untuk melewati semua konfirmasi. Template-nya tinggal di folder di sebelah script, jadi generator membawa semua yang dibutuhkannya.

Cara saya memeriksa tool

Inilah pertanyaan yang saya ajukan kepada tool yang sudah selesai, diambil dari kesepakatan di atas:

BidangPertanyaan yang saya ajukanSumber
KontrakApakah baris-baris pertama menyatakan apa yang dikerjakan tool dan arti tiap exit code?kontrak di bagian atas
UrutanApakah seksinya berada dalam urutan tetap, masing-masing bertanda banner?urutan seksi
KonfigurasiApakah tiap field divalidasi sebelum apa pun disambungkan, dengan exit code sendiri bila gagal?seksi Config
InputApakah tiap jenis nilai punya tepat satu pintu masuk?tiga pintu masuk
LaporanApakah tiap check mencetak satu baris, dan apakah ringkasannya menghitungnya?Reporter dan Summary
RahasiaApakah ada contoh konfigurasi berisi placeholder, dan apakah yang sungguhan dijauhkan dari repository?file konfigurasi
AutomationBisakah tool jalan tanpa orang, lewat argumen, konfigurasi, atau environment variable?varian Bash dan exit code
Pertanyaan yang saya pakai untuk memeriksa tool satu file.

Di mana saya tidak memakainya

Ketika tool tumbuh menjadi service

Tool yang mulai memegang beberapa tugas, melayani request, atau perlu hidup dan dirawat bertahun-tahun sudah terlalu besar untuk satu file. Pada titik itu lapisan yang tadinya membebani justru sepadan dengan ongkosnya, dan saya memindahkannya ke Clean Architecture, tempat aturannya dipisahkan dari tool di sekelilingnya.

Rujukan

Arsitektur ini tak punya buku di belakangnya; ini kesepakatan yang saya bangun dari tool-tool saya sendiri. Dua bagiannya mengikuti kesepakatan lama program command-line Unix: exit code 0 untuk sukses dan bukan nol untuk gagal, serta 130 untuk jalan yang disela Ctrl+C, yang dilaporkan shell sebagai 128 ditambah nomor signal interupsinya.

Istilah

Exit code
Angka yang dikembalikan program saat berakhir. Script memakainya untuk membedakan sukses dari berbagai jenis kegagalan tanpa membaca output.
File konfigurasi
File, di sini JSON, yang menggambarkan satu target yang dikerjakan tool, misalnya satu server beserta kredensialnya.
Prompt interaktif
Pertanyaan yang diajukan tool di terminal selama ia berjalan, untuk nilai yang berubah tiap kali jalan.
Environment variable
Nilai bernama yang di-set di luar program dan bisa dibacanya, sehingga script bisa memberi jawaban tanpa diketik.
Generator
Script yang membuat folder siap pakai, misalnya deployment stack, dari template dan beberapa jawaban.

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Referenced by

A stack of Markdown documents and an orange arrow leading to a browser page with a side menu, a heading, paragraphs, a dark code panel, and a table of contents with its active entry highlighted

Satu script yang mengubah dokumentasi Markdown satu project menjadi situs statis yang terbuka dari folder tanpa server maupun internet: menu, daftar isi yang mengikuti pembaca, tabel yang tak pernah terpotong, kode dengan tombol salin, dan tautan yang tetap berfungsi.

7 min read Read more →
An old layout with a grey banner and a new structure with a green banner, each holding a blue database, a purple document store, and an orange telemetry store; three coloured paths pass through one export package marked with a green check

Empat perintah yang memindahkan stack dari layout lama ke struktur baru melintasi versi mayor: export tanpa mengubah data sumber, import tiap store sesuai cara yang diizinkan versinya, verifikasi apa yang benar-benar tiba, dan rename modul sambil memindahkan datanya.

11 min read Read more →
Three image layer stacks, each with a version tag of its own colour, blue, orange, and green, each leading to an archive file banded in the same colour

Perintah yang memberi versi tersendiri pada tiap modul stack, mem-build image-nya di bawah tag yang berarti tepat satu build, menolak memakai ulang versi atau menelan compile yang gagal, dan mengarsipkan image menurut nama dan tag supaya versi tak saling menimpa.

7 min read Read more →
Four identical grey keys on the left each become a key of its own colour on the right, every new key marked with a green check; below, a purple key stays behind a red bar

Satu perintah yang merotasi kredensial internal satu environment kunci demi kunci: dry run sebagai default, backup lebih dulu, nilai unik untuk tiap kunci, login sungguhan untuk membuktikan tiap kredensial yang bisa dipakai login, dan rollback bagi yang gagal.

8 min read Read more →
A browser window sends one orange path into a single web server, which routes it on to the orange staging environment; dashed paths to the blue development and green production environments stay inactive

Dua perintah yang menjaga tiga environment tetap terpisah di satu mesin dan satu web server: satu menetapkan alamat environment dan menyelaraskan setiap file yang menyimpannya, merender nginx dari template dan menolak bila bentrok; satu lagi mengarahkan browser ke satu environment.

11 min read Read more →
A staging configuration with an orange banner and a production configuration with a green banner; ordinary keys cross over with arrows, while a locked secret is stopped halfway by a red cross and production keeps its own lock

Tiga perintah yang membawa rilis teruji ke production tanpa menimpa apa yang dimilikinya: mempromosikan struktur konfigurasi tapi mempertahankan rahasia dan alamatnya, menolak menyalin rahasia baru, dan menerapkan migration yang dibawa image.

10 min read Read more →
Three sources on the left — configuration, container images, and a backup — flowing into one staging environment with an orange banner, its services, and a healthy check mark

Tiga perintah yang membangun salinan penuh production untuk satu rilis — konfigurasi dari repository, image dari registry, data dari backup terbaru — me-restore-nya berurutan, menjalankan migration rilis terhadap data itu, dan membuangnya tanpa jejak.

13 min read Read more →
A database, an object storage bucket, and a telemetry store on the left, each flowing into a stack of restore points on the right, the front one showing a timeline and a check mark

Tiga perintah yang mem-backup stack Docker yang berjalan — PostgreSQL, MinIO, dan Elasticsearch — tanpa downtime, me-restore-nya ke titik mana pun, dan menjaga retensi tetap aman, dengan exit code yang menyatakan bobot kegagalannya.

12 min read Read more →
A checklist card on the left, a three-part token in the middle with a check mark, and an identity provider marked with a key on the right

Script Python interaktif yang membuktikan identity provider siap untuk satu client: ia menggladi login dan logout PKCE utuh seperti aplikasi, lalu memeriksa token yang diterimanya terhadap key milik provider sebagaimana yang wajib dilakukan aplikasi.

11 min read Read more →
A terminal window listing passing checks, connected to a blue storage bucket with a padlock and a green storage bucket without one.

Satu script Python yang membuktikan storage kompatibel S3 siap sebelum aplikasi bergantung padanya, dengan memakai tiap bucket persis seperti client sungguhan dan menangkap kegagalan yang tetap menjawab HTTP 200.

12 min read Read more →
Four concentric circles with arrows coming in from a database, a browser window, and a message queue, all pointing toward the centre.

Clean Architecture

Programming

Clean Architecture adalah salah satu arsitektur yang saya pakai: kenapa saya memilihnya, kesepakatan folder dan penamaan yang diikuti tiap module saya di Rust, Python, Dart, Node.js, dan C, pertanyaan yang saya pakai untuk memeriksa module, dan tempat saya memakai arsitektur lain.

12 min read Read more →

Related Articles

A stack of Markdown documents and an orange arrow leading to a browser page with a side menu, a heading, paragraphs, a dark code panel, and a table of contents with its active entry highlighted

Satu script yang mengubah dokumentasi Markdown satu project menjadi situs statis yang terbuka dari folder tanpa server maupun internet: menu, daftar isi yang mengikuti pembaca, tabel yang tak pernah terpotong, kode dengan tombol salin, dan tautan yang tetap berfungsi.

7 min read Read more →
An old layout with a grey banner and a new structure with a green banner, each holding a blue database, a purple document store, and an orange telemetry store; three coloured paths pass through one export package marked with a green check

Empat perintah yang memindahkan stack dari layout lama ke struktur baru melintasi versi mayor: export tanpa mengubah data sumber, import tiap store sesuai cara yang diizinkan versinya, verifikasi apa yang benar-benar tiba, dan rename modul sambil memindahkan datanya.

11 min read Read more →
Three image layer stacks, each with a version tag of its own colour, blue, orange, and green, each leading to an archive file banded in the same colour

Perintah yang memberi versi tersendiri pada tiap modul stack, mem-build image-nya di bawah tag yang berarti tepat satu build, menolak memakai ulang versi atau menelan compile yang gagal, dan mengarsipkan image menurut nama dan tag supaya versi tak saling menimpa.

7 min read Read more →