Pengembangan Web

Composer Lock File untuk Aplikasi PHP Kecil - Deploy Dependency yang Sudah Ditinjau

Composer Lock File untuk Aplikasi PHP Kecil - Deploy Dependency yang Sudah Ditinjau

Sebuah aplikasi PHP bisa berhasil di-deploy pada hari Senin lalu gagal pada hari Jumat meski source code-nya tidak berubah. Salah satu kemungkinan penyebabnya mudah terlewat: kedua deployment tersebut tidak menerima versi dependency yang sama. Version constraint seperti ^3.0 menjelaskan sebuah rentang, bukan satu rilis package yang tidak berubah.

Composer menangani masalah ini dengan composer.lock. Lock file cukup sederhana untuk di-commit lalu dilupakan, tetapi kesederhanaan itu bisa menyembunyikan dua kekeliruan yang berlawanan: mengabaikannya sama sekali, atau menganggapnya sebagai bukti bahwa seluruh environment production dapat direproduksi. Keduanya kurang tepat.

Artikel ini mengajukan pertanyaan yang lebih sempit: bagaimana aplikasi PHP kecil dapat men-deploy kumpulan dependency yang benar-benar sudah ditinjau, sambil tetap jujur mengenai hal-hal yang tidak dikendalikan lock file?

Anggap manifest sebagai kebijakan dan lock sebagai snapshot

composer.json mendeklarasikan apa saja yang boleh digunakan project. File ini dapat menetapkan kebutuhan PHP, extension, package aplikasi, development tool, dan rentang versi. Composer kemudian menyelesaikan semua constraint itu bersama setiap transitive dependency.

composer.lock mencatat hasil resolution tersebut. Untuk sebuah aplikasi, mental model yang berguna adalah:

  • composer.json adalah kebijakan dependency.
  • composer.lock adalah snapshot hasil pilihan yang telah ditinjau berdasarkan kebijakan tersebut.
  • vendor/ adalah instalasi snapshot itu untuk sebuah rilis tertentu.

Ini merupakan model penjelasan, bukan istilah resmi Composer, tetapi model tersebut menangkap perbedaan penting. Menurut dokumentasi basic usage Composer, update melakukan dependency resolution dan menulis versi persisnya ke lock file. Ketika lock file sudah tersedia, install memakai versi yang tercatat di dalamnya.

Untuk aplikasi, lock file biasanya perlu di-commit bersama source. Dengan begitu, komputer developer, job CI, dan proses deployment dapat meminta versi package yang sama. Reusable library memiliki pertimbangan berbeda karena konsumennya yang melakukan dependency resolution, tetapi hal itu berada di luar cakupan artikel ini.

update mengambil keputusan; install menerapkannya

Nama kedua command tersebut terdengar mirip, tetapi keduanya berada pada tahap yang berbeda.

composer update meminta Composer menyelesaikan kembali dependency graph. Sebuah constraint mungkin sekarang memilih package yang lebih baru, dan transitive package juga bisa berubah. Karena itu, diff lock file yang dihasilkan menjadi bagian dari perubahan yang diajukan. Diff tersebut layak ditinjau dan diuji seperti diff pada application code.

composer install, ketika diberi lock file, menerapkan pilihan yang sudah tercatat. Menjalankan update langsung saat deployment menyatukan dua peristiwa yang lebih aman bila dipisahkan: memilih third-party code baru dan merilis aplikasi. Deployment yang gagal kemudian lebih sulit dijelaskan karena server mengambil keputusan dependency yang tidak ada dalam commit yang sudah ditinjau.

Workflow sederhana dapat berbentuk seperti ini:

  1. Jalankan dependency update secara sengaja di development branch atau maintenance change.
  2. Tinjau composer.json dan composer.lock.
  3. Jalankan test aplikasi dan smoke check yang relevan.
  4. Commit lock file bersama perubahannya.
  5. Deploy commit tersebut dengan composer install, bukan composer update yang belum ditinjau.

Cara ini tidak membuat update bebas risiko. Ia membuat keputusan tersebut terlihat, dapat diuji, dan lebih mudah dibalik.

Urutan deployment sederhana

Proses rilis yang tepat bergantung pada aplikasinya, tetapi command berikut menunjukkan tanggung jawab yang perlu diwakili di suatu tempat dalam pipeline. Command sengaja dipisahkan; sistem deployment perlu berhenti ketika gate yang diwajibkan gagal.

1. Validasi manifest dan lock

composer validate --strict

Referensi Composer CLI menyatakan bahwa validate memeriksa composer.json dan, bila lock file tersedia, memeriksa apakah file itu mutakhir terhadap manifest. --strict juga membuat warning menghasilkan exit status bukan nol. Ini merupakan consistency gate yang berguna, bukan pengganti test aplikasi.

2. Install kumpulan production yang sudah ditinjau

composer install --no-dev --optimize-autoloader --no-interaction

--no-dev melewatkan package di require-dev dan development autoload rules. Hal ini mengurangi dependency khusus development di production, tetapi juga mengungkap kesalahan konfigurasi bila runtime code diam-diam bergantung pada development package. Kegagalan seperti itu sebaiknya diperbaiki pada deklarasi dependency, bukan disembunyikan dengan memasang semua development tool di production.

--optimize-autoloader mengubah aturan PSR-0 dan PSR-4 menjadi class map. Panduan autoloader optimization Composer merekomendasikan optimization level pertama ini untuk production. Opsi --classmap-authoritative yang lebih agresif bukan upgrade universal: panduan yang sama memperingatkan bahwa opsi tersebut dapat merusak software yang menghasilkan class saat runtime.

--no-interaction mencegah deployment menunggu input terminal tanpa batas. Opsi ini tidak otomatis menjawab setiap pertanyaan operasional; izin plugin dan authentication tetap membutuhkan keputusan eksplisit yang telah dikonfigurasi sebelumnya.

3. Periksa platform runtime yang sebenarnya

composer check-platform-reqs --no-dev

Composer merepresentasikan PHP, extension, dan beberapa system library sebagai platform package. Lock file dapat mempertahankan versi package sementara server tujuan tetap tidak memiliki ext-intl, menjalankan versi PHP yang tidak kompatibel, atau berbeda dalam kemampuan platform lain yang dibutuhkan.

Dokumentasi check-platform-reqs menyatakan bahwa command ini memeriksa environment PHP dan extension yang nyata serta mengabaikan nilai simulasi config.platform. Command tersebut sebaiknya dijalankan pada target deployment atau final runtime image, bukan hanya pada laptop developer.

4. Jalankan pemeriksaan khusus aplikasi sebelum mengalihkan traffic

Tidak ada command Composer generik yang dapat membuktikan bahwa route dapat dirender, akses database berfungsi, konfigurasi tersedia, atau queue consumer bisa dimulai. Sebuah rilis tetap memerlukan pemeriksaan yang sesuai dengan aplikasinya. Untuk situs kecil, pemeriksaan itu mungkin berupa health endpoint, satu operasi database read-only, dan request ke beberapa halaman penting.

Jika memungkinkan, bangun rilis baru dalam direktori terpisah dan alihkan ke sana hanya setelah pemeriksaan ini lulus. Mempertahankan rilis sebelumnya membuat rollback menjadi perubahan direktori atau symlink, bukan upaya menyusun ulang dependency graph kemarin di tengah insiden.

Platform setting adalah target, bukan bukti

Opsi config.platform milik Composer dapat menyimulasikan versi PHP atau extension target selama dependency resolution. Ini berguna ketika development menjalankan versi PHP yang lebih baru daripada production: resolver dapat dicegah memilih package yang membutuhkan lebih dari kemampuan production.

Namun, platform yang disimulasikan juga dapat menyembunyikan kenyataan. Jika konfigurasi menyatakan PHP 8.x sementara sebuah command sebenarnya berjalan dengan interpreter yang lebih lama, resolution mungkin berhasil lalu runtime gagal. Karena itu, Composer merekomendasikan agar setting ini dipasangkan dengan check-platform-reqs.

Ada dua pertanyaan terpisah:

  • Apakah kumpulan dependency diselesaikan untuk target yang dimaksud?
  • Apakah mesin yang akan menjalankannya benar-benar memenuhi target tersebut?

config.platform membantu menjawab pertanyaan pertama. Pemeriksaan terhadap runtime sebenarnya membantu menjawab pertanyaan kedua.

Script dan plugin membuat instalasi lebih dari sekadar menyalin file

composer install mudah digambarkan hanya sebagai proses mengunduh archive. Gambaran itu tidak lengkap. Composer mendukung script dan plugin, dan security FAQ-nya menjelaskan bahwa keduanya dapat mengeksekusi third-party code dengan hak akses account yang menjalankan Composer.

Hal ini membawa dua perhatian praktis. Pertama, hindari menjalankan Composer sebagai root hanya karena deployment perlu menulis file; berikan dedicated deployment account hak akses sempit yang dibutuhkannya. Kedua, pahami script dan plugin yang diharapkan aplikasi. Flag --no-scripts dan --no-plugins mengurangi eksekusi code, tetapi mengaktifkannya secara membabi buta dapat melewatkan setup aplikasi yang diperlukan. Keduanya merupakan pilihan kebijakan, bukan switch hardening untuk sekadar ditambahkan.

Lock file membuat kumpulan package yang dipilih dapat diperiksa. File itu tidak membuat setiap package terpilih menjadi tepercaya, dan tidak menetralkan code yang secara sah berjalan selama instalasi.

Hal-hal yang tidak dikunci lock file

Composer mendokumentasikan bahwa instalasi berulang dari lock yang sama dapat menghasilkan isi vendor/ yang identik kecuali timestamp. Itu merupakan jaminan yang bernilai dan cukup spesifik. Jaminan tersebut tidak semestinya diperluas menjadi “semua server identik.”

Lock file sendiri tidak membekukan:

  • PHP interpreter dan konfigurasinya;
  • PHP extension yang terpasang dan system library yang ditautkan;
  • Composer executable yang digunakan pipeline;
  • operating-system package, environment variable, secret, dan konfigurasi service;
  • database schema dan persistent data;
  • external service yang dipanggil aplikasi;
  • efek Composer script, plugin, atau application build step.

Deployment yang lebih kuat mencatat atau mengendalikan lapisan-lapisan tersebut secara terpisah: misalnya pinned container base atau daftar server package yang terdokumentasi, rilis Composer yang diketahui, schema migration, configuration validation, dan release artifact yang dapat disimpan. Tidak setiap situs personal membutuhkan artifact platform yang rumit, tetapi setiap situs terbantu dengan mengetahui asumsi mana yang masih berada di luar Composer.

Terkunci bukan berarti terawat

Lock file sengaja mencegah upgrade yang mengejutkan. Stabilitas yang sama dapat mempertahankan dependency lama yang rentan atau abandoned jika tidak ada yang meninjau update. Repeatability dan maintenance adalah dua tujuan berbeda.

Dependency update perlu dilakukan secara sengaja, bukan dihindari selamanya. Composer menyediakan command outdated dan audit untuk mendukung review, tetapi output-nya tetap membutuhkan konteks. Sebuah advisory mungkin berpengaruh atau tidak berpengaruh terhadap cara aplikasi menggunakan package, sedangkan update yang tampak minor tetap mungkin layak diuji. Kebiasaan yang berguna adalah maintenance window berkala: periksa perubahan yang tersedia, update kumpulan yang dibatasi, lakukan test, tinjau lock diff, lalu rilis.

Rollback juga membutuhkan batas yang jelas. Mengembalikan lock file lama lalu melakukan install ulang ketika outage masih bergantung pada ketersediaan repository dan environment. Menyimpan release artifact yang sebelumnya berfungsi biasanya menjadi pilihan yang lebih tenang. Jika sebuah rilis menyertakan database migration yang tidak dapat dibalik, mengembalikan file PHP saja mungkin tidak aman, sehingga dependency rollback harus sesuai dengan migration plan aplikasi yang lebih luas.

Janji yang sempit tetap merupakan janji yang berguna

composer.lock tidak dapat membuat deployment PHP lengkap menjadi reproducible sendirian. File itu dapat melakukan sesuatu yang lebih kecil dan penting: mempertahankan keputusan dependency yang telah ditinjau dan diuji.

Pola operasionalnya cukup jelas. Lakukan update resolution di luar production, commit dan tinjau lock file, validasi file tersebut, lakukan install alih-alih update saat deployment, periksa platform runtime yang nyata, jalankan smoke test khusus aplikasi, dan pertahankan jalur rollback. Setelah itu, dokumentasikan variabel yang tersisa daripada berpura-pura semua variabel tersebut telah hilang.

Janji yang lebih sempit itu bukan kelemahan. Ia menghilangkan satu pertanyaan yang tidak perlu dijawab deployment pada saat terburuk: “Versi dependency mana yang dipilih server hari ini?”

References