<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jonas Siewertsen</title>
    <description>The latest articles on Forem by Jonas Siewertsen (@jonassiewertsen).</description>
    <link>https://forem.com/jonassiewertsen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F602193%2F0836bc4b-10dd-42fd-8e3b-d826da2da276.png</url>
      <title>Forem: Jonas Siewertsen</title>
      <link>https://forem.com/jonassiewertsen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jonassiewertsen"/>
    <language>en</language>
    <item>
      <title>Laravel logging: Show correct severity levels on the Google Cloud Platform</title>
      <dc:creator>Jonas Siewertsen</dc:creator>
      <pubDate>Sun, 05 Feb 2023 20:00:00 +0000</pubDate>
      <link>https://forem.com/jonassiewertsen/laravel-logging-show-correct-severity-levels-on-the-google-cloud-platform-37cn</link>
      <guid>https://forem.com/jonassiewertsen/laravel-logging-show-correct-severity-levels-on-the-google-cloud-platform-37cn</guid>
      <description>&lt;p&gt;Let's get right to the point: If you're running a containerized Laravel application on the Google Cloud platform, you can use &lt;code&gt;stderr&lt;/code&gt; as your log channel. &lt;a href="https://github.com/laravel/laravel/blob/9.x/config/logging.php#L92-L100" rel="noopener noreferrer"&gt;See on GitHub&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;That does work, but every log entry will be shown with the same severity level. Depending on your setup, all log entries will either be shown as &lt;code&gt;default&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt;.&lt;br&gt;
All the information is there, but you can't filter for severity levels, which is not what you want and makes it hard to spot important error messages or does make it harder to monitor them.&lt;/p&gt;

&lt;p&gt;See the example below. Can you spot errors or critical logs?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghjcye7i05cbn8r20o9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fghjcye7i05cbn8r20o9y.png" alt="All Laravel logs do show the same severity level" width="800" height="236"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why not the official PHP Google logging Package?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://cloud.google.com/logging/docs/setup/php" rel="noopener noreferrer"&gt;Official PHP Package from Google&lt;/a&gt; does work and will ensure severity levels are set correctly.&lt;/p&gt;

&lt;p&gt;That package does send all logs via HTTP, which is a little less unstable than logging via &lt;code&gt;stderr&lt;/code&gt; on Unix systems.&lt;/p&gt;

&lt;p&gt;The package can also send your logs via gRPC, but to enable gRPC support, you need to install the gRPC extension through PECL.&lt;/p&gt;

&lt;p&gt;Don't get me wrong: it's a great package, but we wanted to keep dependencies as low as possible.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why are all logs shown with the same severity level?
&lt;/h2&gt;

&lt;p&gt;This topic is more complex, as there might be different reasons, depending on your setup.&lt;/p&gt;

&lt;p&gt;In the end, Google Cloud can't interpret the severity levels correctly. To fix that, we can send plain JSON via &lt;code&gt;stderr&lt;/code&gt; or &lt;code&gt;stdout&lt;/code&gt; and Google Cloud Logging will parse that information.&lt;/p&gt;

&lt;p&gt;An example payload that Google can work with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ERROR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"There was an error in the application."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about it in the &lt;a href="https://cloud.google.com/logging/docs/structured-logging" rel="noopener noreferrer"&gt;Google Docs&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to normalize all logs into JSON?
&lt;/h2&gt;

&lt;p&gt;Laravel (and many other frameworks) use the &lt;a href="https://github.com/Seldaek/monolog" rel="noopener noreferrer"&gt;Monolog Package&lt;/a&gt; for logging by default. Using their formatter does everything you need!&lt;/p&gt;

&lt;h2&gt;
  
  
  Monolog is your friend
&lt;/h2&gt;

&lt;p&gt;Instead of only sending the error message, we'll send the logs as JSON with some extra information so the Google Cloud Logging can parse your information correctly.&lt;/p&gt;

&lt;p&gt;Logs before formatting. &lt;/p&gt;

&lt;h2&gt;
  
  
  Enable the correct formatter from Monolog.
&lt;/h2&gt;

&lt;p&gt;Monolog offers a &lt;code&gt;GoogleCloudLoggingFormatter&lt;/code&gt; by default. Let's enable it in the config.&lt;/p&gt;

&lt;p&gt;The default stderr settings inside &lt;code&gt;config/logging.php&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'stderr'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'monolog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'level'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LOG_LEVEL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'debug'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'handler'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;StreamHandler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'formatter'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LOG_STDERR_FORMATTER'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'with'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'stream'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'php://stderr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define the Monolog formatter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'stderr'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'monolog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'level'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LOG_LEVEL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'debug'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'handler'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;StreamHandler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'formatter'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\Monolog\Formatter\GoogleCloudLoggingFormatter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'with'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'stream'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'php://stderr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. You can also set it via the env variable if you prefer that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LOG_STDERR_FORMATTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\M&lt;/span&gt;onolog&lt;span class="se"&gt;\F&lt;/span&gt;ormatter&lt;span class="se"&gt;\G&lt;/span&gt;oogleCloudLoggingFormatter::class
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Write your own and custom formatter.
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;GoogleCloudLoggingFormatter&lt;/code&gt; has been added in the Monolog versions 2.8.0 and 3.2.0.&lt;/p&gt;

&lt;p&gt;If your version is older than that or you need to customize it, you can write your own custom formatter. &lt;/p&gt;

&lt;p&gt;Your custom formatter might look like this (&lt;a href="https://github.com/Seldaek/monolog/blob/2.x/src/Monolog/Formatter/GoogleCloudLoggingFormatter.php" rel="noopener noreferrer"&gt;See here&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Support&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;DateTimeInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GoogleCloudLoggingFormatter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;\Monolog\Formatter\JsonFormatter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Re-key level for GCP logging&lt;/span&gt;
        &lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'severity'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'level_name'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'timestamp'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'datetime'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DateTimeInterface&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;RFC3339_EXTENDED&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Remove keys that are not used by GCP&lt;/span&gt;
        &lt;span class="k"&gt;unset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'level'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'level_name'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'datetime'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a last step, you need to define that class inside &lt;code&gt;config/logging.php&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="s1"&gt;'stderr'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'driver'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'monolog'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'level'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LOG_LEVEL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'debug'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'handler'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;StreamHandler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'formatter'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;\App\Support\GoogleCloudLoggingFormatter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'with'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'stream'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'php://stderr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  This is what correct formatted logs look like
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F701oedyn79sjduc5njya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F701oedyn79sjduc5njya.png" alt="All Laravel logs do show the correct severity levels." width="658" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isn't that beautiful? Even without further dependencies besides Monolog.&lt;/p&gt;




&lt;h3&gt;
  
  
  Credits
&lt;/h3&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/fr/@etiennegirardet?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Etienne Girardet&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/T_fYMj6H-pE?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Published at &lt;a href="https://jonassiewertsen.com/blog/laravel-logging-show-correct-severity-levels-on-the-google-cloud-platform" rel="noopener noreferrer"&gt;https://jonassiewertsen.com/blog/laravel-logging-show-correct-severity-levels-on-the-google-cloud-platform&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gratitude</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Why to use »npm ci« instead of »npm install«</title>
      <dc:creator>Jonas Siewertsen</dc:creator>
      <pubDate>Wed, 28 Apr 2021 15:02:14 +0000</pubDate>
      <link>https://forem.com/visuellverstehen/why-to-use-npm-ci-instead-of-npm-install-mfd</link>
      <guid>https://forem.com/visuellverstehen/why-to-use-npm-ci-instead-of-npm-install-mfd</guid>
      <description>&lt;p&gt;We had some problems with our pipelines/actions lately, which could easily be resolved by using &lt;code&gt;npm ci&lt;/code&gt; instead of &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;npm install&lt;/code&gt; to update and install your dependencies.&lt;br&gt;
Use &lt;code&gt;npm ci&lt;/code&gt; to only install your dependencies.&lt;/p&gt;

&lt;p&gt;Always use &lt;code&gt;npm ci&lt;/code&gt; in your pipelines/actions, never &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t compare it with composer
&lt;/h2&gt;

&lt;p&gt;Personally, I do have a strong PHP background and am using composer as PHP’s Package dependency manager on a daily basis.&lt;/p&gt;

&lt;p&gt;One of the first things you would learn about composer, is the difference between the install and update command:&lt;br&gt;
&lt;code&gt;composer update&lt;/code&gt; will read your composer.json, update all dependencies, write those in your composer.lock file and install them afterwards.&lt;/p&gt;

&lt;p&gt;In case you only want to install your packages as locked in your composer.lock file, use &lt;code&gt;composer install&lt;/code&gt; instead. This will ensure, that your production environment uses the same dependency versions.&lt;/p&gt;

&lt;p&gt;That does make sense, right? This is what we want to use in our pipelines or actions (depending which kind of CI/CD you are using). This does make sure to not auto-update dependencies when deploying. Maybe some updated ones might break something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s transfer this knowledge to npm
&lt;/h2&gt;

&lt;p&gt;Do we agree, that we want to update dependencies locally, lock those dependencies in our lock-file and only want to install those dependencies as we did lock them?&lt;/p&gt;

&lt;p&gt;This does avoid any auto-update which might break something in you application. That’s the exact reason, &lt;code&gt;composer install&lt;/code&gt; is being widely used in all production pipelines that I have seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;npm install&lt;/code&gt; does work in a different way than &lt;code&gt;composer install&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the key element to understand! Those commands are not the same. Let me explain:&lt;/p&gt;

&lt;p&gt;Unlike &lt;code&gt;composer install&lt;/code&gt;, &lt;code&gt;npm install&lt;/code&gt; will update your dependencies before installing. This isn’t logical at all when you have a PHP background, but it’s important to understand and you need to deal with it.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;npm update&lt;/code&gt; are nearly the same and will update your &lt;code&gt;package-lock.json&lt;/code&gt;, which is what you want to avoid in production.&lt;/p&gt;

&lt;p&gt;If you want the same functionality as &lt;code&gt;composer install&lt;/code&gt;, use &lt;code&gt;npm ci&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;npm ci&lt;/code&gt; if you don’t want to update
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npm ci&lt;/code&gt; will parse your &lt;code&gt;package-lock.json&lt;/code&gt;, won’t do any updates and install those packages.&lt;/p&gt;

&lt;p&gt;This is exactly the same as using &lt;code&gt;composer install&lt;/code&gt; in the PHP world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Please update your Pipeline or Actions
&lt;/h2&gt;

&lt;p&gt;If you do use any kind of Pipelines or Actions, make sure to use &lt;code&gt;npm ci&lt;/code&gt;, to avoid any problems.&lt;/p&gt;

&lt;p&gt;This will avoid merge conflicts. Just in case: Yes, you should version control your package-lock.json file, so every developer working with your project will use the same dependencies as you do and as your production environment does.&lt;/p&gt;

&lt;p&gt;Besides this and other occurring problems, it will make your pipelines and actions faster, as &lt;code&gt;npm ci&lt;/code&gt; will be finished in less time than &lt;code&gt;install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I hope this was helpful. &lt;/p&gt;




&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@romanenko29061983"&gt;https://unsplash.com/@romanenko29061983&lt;/a&gt;&lt;/p&gt;

</description>
      <category>npm</category>
      <category>devops</category>
      <category>php</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
