<?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>DEV Community: Nick Vahalik</title>
    <description>The latest articles on DEV Community by Nick Vahalik (@nvahalik).</description>
    <link>https://dev.to/nvahalik</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%2F290516%2F07043055-c9a1-49b0-b9fb-d246dda62282.jpeg</url>
      <title>DEV Community: Nick Vahalik</title>
      <link>https://dev.to/nvahalik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nvahalik"/>
    <language>en</language>
    <item>
      <title>You may not need pg_vector, sqlite-vss, etc.</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Thu, 06 Nov 2025 02:10:06 +0000</pubDate>
      <link>https://dev.to/nvahalik/you-may-not-need-pgvector-sqlite-vss-etc-e6j</link>
      <guid>https://dev.to/nvahalik/you-may-not-need-pgvector-sqlite-vss-etc-e6j</guid>
      <description>&lt;p&gt;I have a little side-project I've been working on for some time. What it does and how it does what it does isn't important. What &lt;em&gt;is&lt;/em&gt; important is that it uses vector embeddings and it &lt;em&gt;does&lt;/em&gt; use &lt;a href="https://towardsdatascience.com/demystifying-cosine-similarity/" rel="noopener noreferrer"&gt;cosine similarity search&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;One of the big stumbling blocks I kept running into over and over again was: how do I store these vectors? And, more importantly, how do I &lt;em&gt;search&lt;/em&gt; these vectors to find matches?&lt;/p&gt;

&lt;p&gt;I tried to get &lt;code&gt;sqlite-vss&lt;/code&gt; and &lt;code&gt;sqlite-vec&lt;/code&gt; both going, but I didn't like having to set the size of the embedding in the Schema definition. &lt;code&gt;pg_vector&lt;/code&gt; forces you to have a single embedding size, but you can truncate the database and re-import and it will only complain about the data if the data you are trying to insert doesn't match the size of the existing vectors in the table.&lt;/p&gt;

&lt;p&gt;Honestly, I spent a lot of time just fooling and fiddling around with the DB. It was truly a stumbling block: I couldn't really find the "right way" to do what I wanted to do. I am &lt;em&gt;still&lt;/em&gt; toying with different embedding models.&lt;/p&gt;

&lt;p&gt;This indecision kept me paralyzed. I just wanted to store my vectors and search them!&lt;/p&gt;

&lt;p&gt;Then it hit me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I don't &lt;em&gt;need&lt;/em&gt; &lt;code&gt;pg_vector&lt;/code&gt;.&lt;/strong&gt; Actually, I don't need a vector-enabled database &lt;em&gt;at all&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;With about 10 minutes and Claude Code, I was able to build a very simple PHP solution that takes a key/value array like this:&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="nv"&gt;$searchContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="s1"&gt;'01K9BDCMXB6SP745NS0C9W9XF9'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="cm"&gt;/* vector */&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="s1"&gt;'01K9BDCJJHXWF8W5VYYD8DAHPS'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="cm"&gt;/* vector */&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="s1"&gt;'01K9BDCE0BRZ51KEYM7WGN6EF2'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="cm"&gt;/* vector */&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="c1"&gt;// More rows.&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And I can search it like this.&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="nv"&gt;$vss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;VSSSearch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$vss&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setSearchContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$searchContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$vss&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="cm"&gt;/* Vector to search */&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: (score: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;The results are the same I'd get from a DB. But I don't &lt;em&gt;need&lt;/em&gt; a DB to do any searching. I just load whatever set of vectors I need and search them. I'm already doing &lt;em&gt;other&lt;/em&gt; stuff after I search anyway. The embedding search is just one step in the process.&lt;/p&gt;

&lt;p&gt;This means I can go back to using MySQL. Storage doesn't complain if the vectors are the wrong size. They are just JSON or binary columns.&lt;/p&gt;

&lt;p&gt;If I need speed I can just cache them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; my search set is only a few hundred records (right now). 10K records is on the extreme high-end probably the most I'd see. For that index size, I don't need &lt;a href="https://towardsdatascience.com/similarity-search-part-4-hierarchical-navigable-small-world-hnsw-2aad4fe87d37/" rel="noopener noreferrer"&gt;HNSW&lt;/a&gt; or &lt;a href="https://github.com/facebookresearch/faiss" rel="noopener noreferrer"&gt;FAISS&lt;/a&gt; or KNN or any of those &lt;em&gt;fancy&lt;/em&gt; indexes. For a few hundred records and/or local development, this is 100% &lt;em&gt;fine&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>php</category>
      <category>rag</category>
      <category>vectordatabase</category>
    </item>
    <item>
      <title>Dealing with Vim errors during Git operations</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Wed, 08 Oct 2025 15:07:45 +0000</pubDate>
      <link>https://dev.to/nvahalik/dealing-with-vim-errors-during-git-operations-4k31</link>
      <guid>https://dev.to/nvahalik/dealing-with-vim-errors-during-git-operations-4k31</guid>
      <description>&lt;p&gt;I use a lot of &lt;code&gt;git&lt;/code&gt; CLI tools and use &lt;code&gt;vim&lt;/code&gt; as my editor. While doing a &lt;code&gt;git rebase&lt;/code&gt;, I'll sometimes accidentally type &lt;code&gt;:wQ&lt;/code&gt; into vim's command entry. However, this isn't a valid command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E492: Not an editor command: wQ
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This aggrieves &lt;code&gt;vim&lt;/code&gt; and it doesn't take such mistakes lightly. It registers this as an error causing a non-zero status code to be emitted upon exit and now &lt;code&gt;git rebase&lt;/code&gt; thinks something has gone horribly wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git rebase -i master
(much editing and such, including an accidental :wQ!)                  
error: there was a problem with the editor 'vi'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have been plagued by this for years and today finally found a solution.&lt;/p&gt;

&lt;p&gt;The issue is one of mental model. Vim isn't just an editor. You can script it. And in some sense, you aren't using it as an editor, it's an editor with a REPL that you can use during editing. That is, your inputs after hitting ⎋ (Escape) will be treated as a live "script" and it's just as if you were running Vim with a script directly.&lt;/p&gt;

&lt;p&gt;In other words, my input (the script) had an error.&lt;/p&gt;

&lt;p&gt;Anyway, the result is that even after successfully &lt;code&gt;:wq!&lt;/code&gt;, Vim still returns a status code of &lt;code&gt;1&lt;/code&gt; and the rebase will fail. Because you screwed up, you have lost all possibility of getting a successful zero-status return code.&lt;/p&gt;

&lt;p&gt;Thankfully, there is a way to work around this. Put this snippet into your &lt;code&gt;~/.vimrc&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;command! WQ wq
command! Wq wq
command! W w
command! Q q
cnoreabbrev wQ wq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, accidentally typing &lt;code&gt;WQ|Wq|W|Q|wQ&lt;/code&gt; will now map to a valid command and all scripting sins are forgiven.&lt;/p&gt;

&lt;p&gt;Now, even if I type &lt;code&gt;:wQ&lt;/code&gt;, vim sees the correct command (&lt;code&gt;:wq&lt;/code&gt;) and saves and exits successfully. Now I can rebase once instead of 3 times. Yay.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>git</category>
    </item>
    <item>
      <title>Getting on the AI bandwagon</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Tue, 01 Jul 2025 23:27:49 +0000</pubDate>
      <link>https://dev.to/nvahalik/getting-on-the-ai-bandwagon-2hp</link>
      <guid>https://dev.to/nvahalik/getting-on-the-ai-bandwagon-2hp</guid>
      <description>&lt;p&gt;If you've been putting off learning AI as a developer, you're behind the curve. There have been plenty of people who have groused about how they won't use AI. Honestly, it comes off as whiny. AI is here to stay whether you like it or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  You don't have to &lt;em&gt;know&lt;/em&gt; AI to use it
&lt;/h2&gt;

&lt;p&gt;Honestly, you just need to jump in. Get a free account. Actually, get several. Just start playing around with it. It's &lt;em&gt;really&lt;/em&gt; great at summarizing long bits of text. You can feed it docs and search them. It's kinda like an ice bath: you kinda just have to get in there and spend some time in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  It doesn't have to write code
&lt;/h2&gt;

&lt;p&gt;Even with something like Claude, it can do a lot of stuff for you without even writing code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It can write tests to give you coverage over classes that don't already have it.&lt;/li&gt;
&lt;li&gt;It can write documentation and generate diagrams in Mermaid.&lt;/li&gt;
&lt;li&gt;You can have it write "plans" for how to do changes.&lt;/li&gt;
&lt;li&gt;It can review plans and suggest changes and improvements.&lt;/li&gt;
&lt;li&gt;You can ask it questions about the code base.&lt;/li&gt;
&lt;li&gt;You can have it analyze the code base to look for areas of improvement.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of these things can help give you better understanding of your existing code base, especially if it's big!&lt;/p&gt;

&lt;h2&gt;
  
  
  Clarity is key!
&lt;/h2&gt;

&lt;p&gt;Unlike a human being, giving an AI a task like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add a column to the user listing on the Coaching page in the admin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;May not give you the results you are looking for. You have to be &lt;em&gt;specific&lt;/em&gt; with AI. You'll need to tell it where you want the column,   how to format it, if it's sortable, etc.&lt;/p&gt;

&lt;p&gt;Which... really, you &lt;em&gt;should&lt;/em&gt; be doing this already, right? If you were lazy before, you won't get the "bump" from AI that you're expecting because you'll need to take the time to be write better expectations and understandings of your work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different tools for different tasks
&lt;/h2&gt;

&lt;p&gt;We use a number of different "AI tools":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.coderabbit.ai" rel="noopener noreferrer"&gt;CodeRabbit&lt;/a&gt; for PR reviews&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://chatgpt.com" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; for a little bit of everything&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://claude.ai/" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; for things like SQL, research, and agentic coding&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jetbrains.com/ai/" rel="noopener noreferrer"&gt;JetBrains AI&lt;/a&gt; for commit messages and code suggestions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jetbrains.com/junie/" rel="noopener noreferrer"&gt;Junie&lt;/a&gt; for helping to "plan" work and doing some light documentation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openai.com" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt; for our user-facing Agentic functionality.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; for local testing and experimentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've experimented personally with half a dozen other ones. It pays to play test and try things out. It's amazing how the quality is so different between various platforms. Claude is excellent at Laravel stuff. ChatGPT is pretty good with SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  It still requires a human at the helm
&lt;/h2&gt;

&lt;p&gt;AI is getting better. But it can't understand intent. It can't read your mind. It can't know that you did something one way because you found a bug in an upstream library. It doesn't "know" that this particular approach works because timeouts are issue. It'll change tests rather than fix the bugs. It'll outright ignore it what you tell it sometimes!&lt;/p&gt;

&lt;p&gt;But like any tool, you must learn it. You must understand its strengths and its weaknesses.&lt;/p&gt;

&lt;p&gt;There are &lt;em&gt;strengths&lt;/em&gt; aplenty, but there are also nuanced drawbacks and differences and changes to your own workflows that will take some time to learn and adapt to.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Running Laravel Seeders before tests</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Wed, 07 May 2025 01:53:51 +0000</pubDate>
      <link>https://dev.to/nvahalik/running-laravel-seeders-before-tests-9kp</link>
      <guid>https://dev.to/nvahalik/running-laravel-seeders-before-tests-9kp</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@cjoudrey?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Christian Joudrey&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/row-of-bean-sprout-aO_jMXTduUE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this post, we’ll break down a custom Laravel service provider I wrote to solve a common but tricky problem: ensuring a fixed, reliable seeding pattern across all test scenarios, especially with parallelization.&lt;/p&gt;




&lt;p&gt;We all know that &lt;a href="https://laravel.com/docs/12.x/migrations#forcing-migrations-to-run-in-production" rel="noopener noreferrer"&gt;deploying database schema changes&lt;/a&gt; is a standard part of the deployment process. But one of the big debates we've had within our own team was how to deploy stuff that &lt;em&gt;wasn't&lt;/em&gt; schema but also &lt;em&gt;wasn't&lt;/em&gt; code.&lt;/p&gt;

&lt;p&gt;The thing is, you &lt;em&gt;could&lt;/em&gt; put data inside of the migration. In fact, there are people that do and it does work for them. But it has limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you're putting your data changes directly in the migration, then you've got data changes scattered in your migrations.&lt;/li&gt;
&lt;li&gt;Those changes aren't applied if you run &lt;code&gt;php artisan schema:dump&lt;/code&gt; (because it only writes out the schema). And they are &lt;strong&gt;gone&lt;/strong&gt; if you tack on &lt;code&gt;--prune&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can write a seeder and centralize the data changes, but that, too, comes with its own problems:

&lt;ul&gt;
&lt;li&gt;Seeders can run multiple times.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;schema:dump&lt;/code&gt; will remove calls to those seeders.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What we are looking for is a solution where:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Seeders will run before tests (e.g. the code can &lt;em&gt;depend&lt;/em&gt; on the data in the seeders).&lt;/li&gt;
&lt;li&gt;Seeders will run during our deployments.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Seeding for success
&lt;/h2&gt;

&lt;p&gt;To us, a successful seeding strategy looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Seeds only code-dependent information. (e.g. Permissions and roles.)&lt;/li&gt;
&lt;li&gt;Runs once before the tests. (Don't re-run the seeder before every test.)&lt;/li&gt;
&lt;li&gt;Doesn't require additional setup. (Don't require devs to run the seeders manually before running tests.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To accomplish this, we created a new Seeder that run 2 sets of seeders: the administrative permissions and our user-focused permissions:&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RequiredPermissionsSeeder&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Seeder&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;run&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AdminPermissionsAndRoles&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SeedUserSubscriptionPermissions&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="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;Getting this to run as part of our deployments was pretty straightforward. In our &lt;code&gt;deploy.php&lt;/code&gt;, just add a task to run the seeder:&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="nf"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'artisan:production-seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;artisan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'db:seed --force --class=RequiredPermissionsSeeder'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'skipIfNoEnv'&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;once&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then just throw it into the &lt;code&gt;deploy&lt;/code&gt; task:&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="nf"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'deploy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// &amp;lt;snip&amp;gt;&lt;/span&gt;
    &lt;span class="s1"&gt;'artisan:migrate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'artisan:production-seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'deploy:publish'&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;Now, we can ensure that our deployments will &lt;em&gt;always&lt;/em&gt; deploy our permissions as long as they are in our seeders. We write pretty comprehensive tests so that's not going to be a problem: so long as the seeders run in the tests we'll &lt;em&gt;know&lt;/em&gt; we added them because we're going to ensure that they are &lt;em&gt;enforced&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running seeders before tests
&lt;/h2&gt;

&lt;p&gt;It's pretty easy to run a seeder before you run a test. Just add this to a &lt;code&gt;setUp()&lt;/code&gt; method:&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="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SomeSeederRequiredByYourTest&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that this seeder will run before every test method. Doing this before a handful of tests isn't a &lt;em&gt;huge&lt;/em&gt; deal, but with over 2000 tests in our suite, running the seeders before each test added a substantial amount of time.&lt;/p&gt;

&lt;p&gt;We need a way to just run the tests before &lt;em&gt;all&lt;/em&gt; the tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work (for me)
&lt;/h2&gt;

&lt;p&gt;I initially asked ChatGPT for help on this and it gave me a number of interesting paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PHPunit Extension&lt;/strong&gt;: Apparently you can run code once before all tests. But... there is still the problem bootstrapping the application. I need something that sits between Laravel and PHPUnit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHPunit bootstap&lt;/strong&gt;: Same issue. I'll still need to bootstrap the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put it into the base class &lt;code&gt;setUp()&lt;/code&gt;&lt;/strong&gt;: Had this worked it would have been easy. But it didn't for reasons I'll get into in a moment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;letDown()&lt;/code&gt; of &lt;code&gt;setUp()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Our applications base &lt;code&gt;TestCase&lt;/code&gt; looks like this:&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="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;BaseTestCase&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;DatabaseTransactions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nc"&gt;MakesJsonApiRequests&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&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;setUp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;LaunchDarkly&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withoutMix&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Other application specific setup.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;parent::setUp()&lt;/code&gt; call must be first because it bootstraps the application. We can't &lt;code&gt;Artisan::call(...)&lt;/code&gt; before it because there won't be a Facade root set.&lt;/p&gt;

&lt;p&gt;However, putting the &lt;code&gt;Artisan::call()&lt;/code&gt; after &lt;code&gt;parent::setUp()&lt;/code&gt; will &lt;strong&gt;wrap the seeders in a transaction&lt;/strong&gt;. That's not gonna work either because it'll run the seeders before every test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's open up &lt;code&gt;setUp()&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setUpTheTestEnvironment&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;Er, we gotta go deeper...&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="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUpTheTestEnvironment&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Facade&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;clearResolvedInstances&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;refreshApplication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;ParallelTesting&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;callSetUpTestCaseCallbacks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setUpTraits&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;afterApplicationCreatedCallbacks&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$callback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setEventDispatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'events'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setUpHasRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;Much better. OK. That first line doesn't look that interesting but... &lt;code&gt;refreshApplication()&lt;/code&gt; and &lt;code&gt;ParallelTesting::callSetUpTestCaseCallbacks()&lt;/code&gt; are &lt;em&gt;both&lt;/em&gt; interesting.&lt;/p&gt;

&lt;p&gt;But what's &lt;code&gt;setupTraits()&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Dig into &lt;code&gt;setupTraits()&lt;/code&gt; and you'll find the &lt;em&gt;real&lt;/em&gt; reason your seeders in &lt;code&gt;setUp()&lt;/code&gt; run in a transaction. Everything after &lt;code&gt;setupTraits()&lt;/code&gt; will be wrapped in a transaction:&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="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;setUpTraits&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$uses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_flip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;class_uses_recursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static&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="c1"&gt;// &amp;lt;snipped&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$uses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;DatabaseTransactions&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;beginDatabaseTransaction&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;So now the question becomes: how do we get it to run during the application "refresh". Well, how do you do &lt;em&gt;anything&lt;/em&gt; when the application boots? A &lt;em&gt;Service Provider&lt;/em&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: &lt;code&gt;TestingServiceProvider&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s walk through the code and see how we tackled this:&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="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Providers&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;Database\Seeders\RequiredPermissionsSeeder&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;Illuminate\Support\Facades\Artisan&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;Illuminate\Support\Facades\ParallelTesting&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;Illuminate\Support\ServiceProvider&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;TestingServiceProvider&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;ServiceProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$hasRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;boot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nf"&gt;app&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;runningUnitTests&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$hasRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$hasRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&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;'LARAVEL_PARALLEL_TESTING'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;ParallelTesting&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setUpTestDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Artisan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'db:seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'--class'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;RequiredPermissionsSeeder&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="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Artisan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'db:seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'--class'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;RequiredPermissionsSeeder&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="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;Let's break this down:&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nf"&gt;app&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;runningUnitTests&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&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;Don't do anything if we aren't in a unit test.&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$hasRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$hasRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the application will get reset between runs, our provider will get initialized multiple times. However, because we are running &lt;em&gt;before&lt;/em&gt; the transactions, we only need to run once. This check just ensures that we only ever run the provider once.&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&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;'LARAVEL_PARALLEL_TESTING'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;ParallelTesting&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;setUpTestDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Artisan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'db:seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'--class'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;RequiredPermissionsSeeder&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parallel Testing in Laravel uses a new database for each process. This database setup happens during bootstrapping. Thankfully, &lt;code&gt;ParallelTesting&lt;/code&gt; gives us a callback that we can use to hook into its setup process. Using &lt;code&gt;ParallelTesting::setUpTestDatabase()&lt;/code&gt; hooks into that so we can run the seeder once per parallel process!&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;key&lt;/strong&gt; part is this: &lt;code&gt;env('LARAVEL_PARALLEL_TESTING') === '1'&lt;/code&gt;. You might think that, "Well, hey, if we can run the seeder once per process, then just running it in the service provider would be enough, right?" Well, it turns out the answer is "no". I cannot tell you why exactly but for some reason, the seeders would somehow manage to produce database errors and would throw odd exceptions.&lt;/p&gt;

&lt;p&gt;Because the test helper sets this env variable, we can know if we are running in a parallel invocation. If we are, then we register our callback with the &lt;code&gt;ParallelTesting&lt;/code&gt; facade. Otherwise... just run it regularly:&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Make sure we run our required seeder once per test suite run.&lt;/span&gt;
    &lt;span class="nc"&gt;Artisan&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'db:seed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'--class'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;RequiredPermissionsSeeder&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the parallel test runner is happy and our regular invocations will also work properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Summary
&lt;/h2&gt;

&lt;p&gt;This was a real head scratcher. However, the cool part is that now our test suite now can have information we depend on in the seeder. The CI test suite runs as expected, and local devs can do their normal work without having to worry about making sure that they seed their single test DB or their parallel testing DBs. The Service Provider takes care of everything.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>testing</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Comparing PHP's Null-safe operator with another null-safe operator can bite you</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Mon, 20 Jan 2025 19:36:41 +0000</pubDate>
      <link>https://dev.to/nvahalik/comparing-phps-null-safe-operator-with-another-null-safe-operator-can-bite-you-4k7p</link>
      <guid>https://dev.to/nvahalik/comparing-phps-null-safe-operator-with-another-null-safe-operator-can-bite-you-4k7p</guid>
      <description>&lt;p&gt;PHP's &lt;a href="https://php.watch/versions/8.0/null-safe-operator" rel="noopener noreferrer"&gt;Null-safe operator&lt;/a&gt; has been a God-send for cutting down long chains of Eloquent relationships. It can multi-line abominations and transform them into simple and elegant checks which are easier for developers to read and more consistent.&lt;/p&gt;

&lt;p&gt;However, they do have dark sides. One problem is trying to figure out if &lt;a href="https://www.exakat.io/en/null-safe-operator-in-practice/" rel="noopener noreferrer"&gt;a chain in the result is null&lt;/a&gt; or not. While following best practices in typing can help in this situation, there is one things that (as of yet) cannot help you.&lt;/p&gt;

&lt;p&gt;Take the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Is the request $user the trainer of the current model's owner?
if ($report-&amp;gt;owner-&amp;gt;linked_trainer?-&amp;gt;trainer_id === $user-&amp;gt;trainer_record?-&amp;gt;id) {
    return true;
}

return false;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At face value, the expression tests for something simple. The comment above is clear: Is the logged in user the trainer of the user who owns this particular model?&lt;/p&gt;

&lt;p&gt;If the current user isn't a trainer, the first part of the right-hand expression (&lt;code&gt;$user-&amp;gt;trainer_record&lt;/code&gt;) will resolve to &lt;code&gt;null&lt;/code&gt;. The left hand expression will be the ID of the user's current trainer. No problem!&lt;/p&gt;

&lt;p&gt;Likewise if the owner doesn't have a trainer, the left-hand expression (&lt;code&gt;$report-&amp;gt;owner-&amp;gt;linked_trainer&lt;/code&gt;) will return &lt;code&gt;null&lt;/code&gt;. This is expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But!&lt;/strong&gt; What if the owner doesn't have a trainer &lt;em&gt;and&lt;/em&gt; the current user isn't a trainer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (null === null) {
    return true;
}

return false;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our permission check fails because &lt;code&gt;null === null&lt;/code&gt;. Ouch.&lt;/p&gt;

&lt;p&gt;I'm currently not aware of a way to check for this, but it's one more thing I'm internally checking for when I do code reviews!&lt;/p&gt;

&lt;p&gt;Do you know of a way to have PHPStan detect this and warn?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Comparing PHP's Null-safe operator with another null-safe operator can bite you</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Mon, 20 Jan 2025 19:36:41 +0000</pubDate>
      <link>https://dev.to/nvahalik/comparing-phps-null-safe-operator-with-another-null-safe-operator-can-bite-you-19o5</link>
      <guid>https://dev.to/nvahalik/comparing-phps-null-safe-operator-with-another-null-safe-operator-can-bite-you-19o5</guid>
      <description>&lt;p&gt;PHP's &lt;a href="https://php.watch/versions/8.0/null-safe-operator" rel="noopener noreferrer"&gt;Null-safe operator&lt;/a&gt; has been a God-send for cutting down long chains of Eloquent relationships. It can multi-line abominations and transform them into simple and elegant checks which are easier for developers to read and more consistent.&lt;/p&gt;

&lt;p&gt;However, they do have dark sides. One problem is trying to figure out if &lt;a href="https://www.exakat.io/en/null-safe-operator-in-practice/" rel="noopener noreferrer"&gt;a chain in the result is null&lt;/a&gt; or not. While following best practices in typing can help in this situation, there is one things that (as of yet) cannot help you.&lt;/p&gt;

&lt;p&gt;Take the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Is the request $user the trainer of the current model's owner?
if ($report-&amp;gt;owner-&amp;gt;linked_trainer?-&amp;gt;trainer_id === $user-&amp;gt;trainer_record?-&amp;gt;id) {
    return true;
}

return false;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At face value, the expression tests for something simple. The comment above is clear: Is the logged in user the trainer of the user who owns this particular model?&lt;/p&gt;

&lt;p&gt;If the current user isn't a trainer, the first part of the right-hand expression (&lt;code&gt;$user-&amp;gt;trainer_record&lt;/code&gt;) will resolve to &lt;code&gt;null&lt;/code&gt;. The left hand expression will be the ID of the user's current trainer. No problem!&lt;/p&gt;

&lt;p&gt;Likewise if the owner doesn't have a trainer, the left-hand expression (&lt;code&gt;$report-&amp;gt;owner-&amp;gt;linked_trainer&lt;/code&gt;) will return &lt;code&gt;null&lt;/code&gt;. This is expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But!&lt;/strong&gt; What if the owner doesn't have a trainer &lt;em&gt;and&lt;/em&gt; the current user isn't a trainer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (null === null) {
    return true;
}

return false;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our permission check fails because &lt;code&gt;null === null&lt;/code&gt;. Ouch.&lt;/p&gt;

&lt;p&gt;I'm currently not aware of a way to check for this, but it's one more thing I'm internally checking for when I do code reviews!&lt;/p&gt;

&lt;p&gt;Do you know of a way to have PHPStan detect this and warn?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Clarifying "Coding Convention"</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Wed, 18 Dec 2024 20:17:22 +0000</pubDate>
      <link>https://dev.to/nvahalik/following-coding-convention-7i9</link>
      <guid>https://dev.to/nvahalik/following-coding-convention-7i9</guid>
      <description>&lt;p&gt;As a team lead, I often find myself, during code reviews, telling the people on my team to "follow convention" when reviewing their PRs. This tends to evoke groans and epithets (many hushed, some audible). What are these "unwritten rules" that I am asking (nay, &lt;em&gt;insisting&lt;/em&gt;? &lt;em&gt;demanding&lt;/em&gt;?) that they follow? &lt;/p&gt;

&lt;p&gt;In some sense, what this looks like for me personally, is that if someone is looking at a project written by some person and myself, they shouldn't be able to tell which parts I wrote and which parts they wrote. What we both write ought to &lt;em&gt;blend in&lt;/em&gt; such that everything is seen as a cohesive whole rather than individual parts mixed together.&lt;/p&gt;

&lt;p&gt;So this post, for me, is an attempt to break down what it means to "follow convention" for myself, my team, and for you, dear reader. Because following convention is more than simply running a linter and exercising proper code formatting. It's about following the leader, being a good team mate, and focusing on what matters by playing by the rules.&lt;/p&gt;

&lt;p&gt;I'm using PHP &amp;amp; TypeScript on a daily basis, so these examples are centered around these languages. The principles should be universally applicable (I think). Let's go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convention, like an ogre, has layers
&lt;/h2&gt;

&lt;p&gt;Convention is hierarchical. Language designers, framework builders, development teams, and module/extension/plugin developers building atop each others' work refines and drives convention. We generally tend to see this form layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local&lt;/li&gt;
&lt;li&gt;Framework&lt;/li&gt;
&lt;li&gt;Language&lt;/li&gt;
&lt;/ol&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%2Ft39v48kjx1ow7rx8uh0g.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%2Ft39v48kjx1ow7rx8uh0g.png" alt="An image showing the mapping of local, framework, and language levels to examples in PHP and TypeScript projects." width="606" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On larger projects, there may be some additional incursions between layers. There may be other external influences, too:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local&lt;/li&gt;
&lt;li&gt;Team&lt;/li&gt;
&lt;li&gt;Project&lt;/li&gt;
&lt;li&gt;Framework&lt;/li&gt;
&lt;li&gt;Language&lt;/li&gt;
&lt;/ol&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%2F7bslnbvh8i2y6zdg3nkr.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%2F7bslnbvh8i2y6zdg3nkr.png" alt="A table showing a mapping of 5-layers to both a Laravel application (and how it deals with permissions) and an Angular app in how it handles state models." width="606" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This list isn't authoritative. Projects of differing complexity will have different numbers of layers. If your project is using a language like Lua, you likely won't have a framework. This post and its examples are written considering Angular and Laravel.&lt;/p&gt;

&lt;p&gt;The list above is from the outer-most layer to the inner-most, but in order to explain, let's look at them from the bottom up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Language convention
&lt;/h3&gt;

&lt;p&gt;Arguably this widest and foundational layer. Basically, this convention says: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;use the language's features (core library and syntax) rather than some other way to accomplish the task at hand&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;TypeScript, for a long time, had the optional-chaining operator. It wasn't natively supported in JS until ES2020 (side note: I didn't know this was a thing until I wrote this.) If it &lt;em&gt;wasn't&lt;/em&gt; in natively part of JS, and you are using TypeScript, then you &lt;em&gt;should&lt;/em&gt; use TS's optional chaining (&lt;code&gt;?.&lt;/code&gt;) because it saves you time and is easy to read.&lt;/p&gt;

&lt;p&gt;Remember, before it was in ES2020, optional-chaining was this in TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maybe&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this in Javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// You could think of it in three ways:&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;//  - A location to learn TypeScript where nothing can break&lt;/span&gt;
&lt;span class="c1"&gt;//  - A place to experiment with TypeScript syntax, and share the URLs with others&lt;/span&gt;
&lt;span class="c1"&gt;//  - A sandbox to experiment with different compiler features of TypeScript&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;maybe&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;maybe&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;maybe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;here&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PHP 8 introduced a number of &lt;code&gt;str_*&lt;/code&gt; functions. Some of these (like &lt;a href="https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions" rel="noopener noreferrer"&gt;&lt;code&gt;str_starts_with()&lt;/code&gt;&lt;/a&gt;) were so needed that the framework themselves built their own and were eventually "pushed down" to the language. So you &lt;em&gt;could&lt;/em&gt; still do this in PHP 8.0:&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strpos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$haystack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$needle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mf"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But you shouldn't. You should use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (str_starts_with($haystack, $needle)) {
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because that's what the language defined method for determining if a string starts with another string.&lt;/p&gt;

&lt;h3&gt;
  
  
  Framework convention
&lt;/h3&gt;

&lt;p&gt;Borrowing from above, Framework convention says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;use the Framework's libraries (core library and syntax) rather than some other way and follow the framework's style and approach rather than your own to solve problems&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good frameworks are support structures and are designed to make you productive, safe, and able to build stuff that works. Like in the Language convention, Framework convention adds opinion to things. It also provides context. Some languages like PHP are "just backend web languages". But some languages like TypeScript or C# can be used anywhere.&lt;/p&gt;

&lt;p&gt;So when you are in a Laravel application, don't write your own router. Use Laravel's. Don't use Symfony's DI. Use Laravel's. &lt;/p&gt;

&lt;p&gt;But if you're working on a Drupal or a Symfony project, you'll want to use the routers, DI, and module/package systems that those frameworks provide.&lt;/p&gt;

&lt;p&gt;Likewise, if a framework makes a preference for a particular decision, you should &lt;em&gt;embrace&lt;/em&gt; that decision. For example, you may love Tailwind. I don't blame you. But if you're working on an Ionic project, you don't want to import and use Tailwind. You've made the decision to use Ionic. You'll want to use Ionic's utility and support classes for styling. Need more? Extend it!&lt;/p&gt;

&lt;p&gt;Frameworks make certain decisions for you for a reason. You don't have to like it, but you should absolutely follow those conventions!&lt;/p&gt;

&lt;h3&gt;
  
  
  Team, Project conventions
&lt;/h3&gt;

&lt;p&gt;In the draft of my original, it was pointed out by a colleague that even within our own project he saw another layer of decisions that our project that had were not "local" but rather drove the decisions of a number of submodules and other parts of the project. He was also directly pointing out that &lt;em&gt;this&lt;/em&gt; layer happens to be the most &lt;strong&gt;cryptic&lt;/strong&gt; and &lt;em&gt;confusing&lt;/em&gt; because this layer is rarely documented. &lt;/p&gt;

&lt;p&gt;We have a corpus of work which we tend to drive from, but we rarely stop to document it. In fact, it's probably &lt;em&gt;this&lt;/em&gt; very layer which (unbeknownst to me at the time) which ultimately started the process of this article being written.&lt;/p&gt;

&lt;p&gt;Here are some examples of Team and Project level conventions we've instituted in our own project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;App\Modules&lt;/code&gt; - Not perfect, but we stick &lt;em&gt;new&lt;/em&gt; bits of functionality into this namespace. The code tends to be (&lt;em&gt;almost&lt;/em&gt;) wholly partitioned off. Things like permissions and navigation don't have a formal hooking/integration... one day!&lt;/li&gt;
&lt;li&gt;Use of &lt;a href="https://github.com/spatie/laravel-permission" rel="noopener noreferrer"&gt;Spatie's Laravel Permission&lt;/a&gt; package - We use this for permissions. Don't try to do any permission work without this!&lt;/li&gt;
&lt;li&gt;MySQL - Some of the decisions we make are directly because of MySQL. So, where some projects may try to write stuff that is DBMS-agnostic, we've leaned into MySQL and don't sweat writing MySQL-specific bits where needed.&lt;/li&gt;
&lt;li&gt;Livewire - We use &lt;a href="https://laravel-livewire.com" rel="noopener noreferrer"&gt;Livewire&lt;/a&gt; for dynamic stuff now. Once over the learning curve, it has made building UIs for our admins simpler, faster, and they are &lt;strong&gt;far more usable!&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Local convention
&lt;/h3&gt;

&lt;p&gt;In larger applications, it's not uncommon to work on modules or sub-projects within the larger project. Sometimes, those parts of the project which are older and potentially follow a slightly different convention. Think about stuff that was maybe written 4 or 5 years ago and hasn't been touched. In those cases:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;use the local code (or module) context's libraries, and approaches rather than some other way and follow the local (or module) style and approach rather than your own&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you find a bug in a piece of OSS software, you'll want the PR to conform to the contribution standards and guidelines. The same goes for stuff you write in a large code base. If you tried to add a second Markdown parser to a project that already had one because &lt;em&gt;you&lt;/em&gt; happened to have a preference for it, you'll get your PR (rightfully) rejected.&lt;/p&gt;

&lt;p&gt;Maybe a module you're working on supports PHP 5.6 for some unholy reason. Your work will need to reflect that reality (though, maybe you should consider finding an alternative, if one exists?).&lt;/p&gt;

&lt;p&gt;There &lt;em&gt;are&lt;/em&gt; exceptions to this. If the thing you are writing is broken, or is blocking a language or framework upgrade, then do what you have to make a decision. You probably &lt;em&gt;shouldn't&lt;/em&gt; rewrite an entire 3-year old controller to add a single feature to it. But if you're writing tests and finding bugs and you find yourself doing a bunch of that work already—talk to a team member. Maybe changing the local convention to match the project convention is warranted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convention hierarchy
&lt;/h2&gt;

&lt;p&gt;That's the model. Bottom to top. In general, the &lt;em&gt;underlying convention&lt;/em&gt; will win. This is true when you write new features. It's true when you upgrade or update functionality.&lt;/p&gt;

&lt;p&gt;Can there be times when a &lt;em&gt;higher&lt;/em&gt; convention overrides something below it? I imagine there can be. But there'd need to be a solid reason for it. And those &lt;em&gt;exceptions&lt;/em&gt; should be treated as such. In an ideal situation, you'd not so much override a foundational convention as improve upon it. &lt;/p&gt;

&lt;p&gt;Note that much of this is about how to write code, but there is a separate piece of all this dealing with things that are more architectural or stylistic... which I'll create in a follow-up to this piece. More on that later.&lt;/p&gt;

&lt;p&gt;By way of contrived example, consider a situation where, in a PHP/Laravel application, you need to parse a CSV:&lt;/p&gt;

&lt;p&gt;Within the project, you need to take some input and generate an array.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The local piece of code doesn't seem to use a library.&lt;/li&gt;
&lt;li&gt;Laravel itself doesn't have a preferred way of handling CSVs.&lt;/li&gt;
&lt;li&gt;PHP has &lt;a href="https://www.php.net/str_getcsv" rel="noopener noreferrer"&gt;&lt;code&gt;str_getcsv&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Minimally, this rules out trying to use some &lt;code&gt;explode()&lt;/code&gt; or &lt;code&gt;preg_match()&lt;/code&gt; to do this. You &lt;em&gt;could&lt;/em&gt; use &lt;code&gt;str_getcsv&lt;/code&gt;. Or... you could go check out a library that solves this for you: like &lt;a href="https://csv.thephpleague.com" rel="noopener noreferrer"&gt;&lt;code&gt;league/csv&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's say you get tasked a few weeks later and you need to update a different Laravel project and you need to implement similar functionality. But let's say that &lt;em&gt;this&lt;/em&gt; project uses &lt;a href="https://laravel-excel.com" rel="noopener noreferrer"&gt;&lt;code&gt;laravel-excel&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By convention, you'd want to learn and use that library, since it's what's there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exceptions
&lt;/h2&gt;

&lt;p&gt;There are always exceptions. Doing refactors and going back and adding tests may constitute a time to do partial or one-off rewrites of some things.&lt;/p&gt;

&lt;p&gt;Regardless of the scope and effort required, the goal should always be to &lt;strong&gt;aim for uniformity of convention&lt;/strong&gt;. If the code base is so large that there isn't a standard uniformity, then maybe you find other problems to solve.&lt;/p&gt;

&lt;p&gt;But if the business needs a new feature and—for sake of argument—that feature is already partially done in a partially-implemented JSON:API endpoint, perhaps the better solution is to argue, for the sake of convention, that the new functionality will require the use of the updated API and you can (finally) deprecate the old API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Convention is one part of this. While &lt;strong&gt;convention&lt;/strong&gt; tends to mean preferring some ways of doing things over others, &lt;strong&gt;standards&lt;/strong&gt; or "best practices" would cover architectural or more overarching topics like listing and CI. Practices tend to live at the Framework level and up. For example, code formatting tends to be framework-dependent. This is totally OK! That's why conceptually it seems to belong in a separate category.&lt;/p&gt;

&lt;p&gt;Feedback is welcome!&lt;/p&gt;

</description>
      <category>coding</category>
      <category>development</category>
      <category>laravel</category>
      <category>angular</category>
    </item>
    <item>
      <title>Use comments to figure out where you're going wrong</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Tue, 04 Aug 2020 03:36:21 +0000</pubDate>
      <link>https://dev.to/nvahalik/use-comments-to-figure-out-where-you-re-going-wrong-104d</link>
      <guid>https://dev.to/nvahalik/use-comments-to-figure-out-where-you-re-going-wrong-104d</guid>
      <description>&lt;p&gt;One of the things that tends to happen to every developer is that you get get stuck with something you're working on. Your code isn't producing the result you are expecting it to.&lt;/p&gt;

&lt;p&gt;What do you do?&lt;/p&gt;

&lt;p&gt;Try this:&lt;/p&gt;

&lt;p&gt;Go through every line of code and write a comment about exactly what it does. Get descriptive. What data is being used, what's the intended result.&lt;/p&gt;

&lt;p&gt;Use a debugger, or just good old &lt;code&gt;print&lt;/code&gt; statements to validate that's what is happening.&lt;/p&gt;

&lt;p&gt;Sometimes the problem is in the expectations: The data that is really necessary is a few lines down, so reorganizing the code may help.&lt;/p&gt;

&lt;p&gt;Sometimes the result you are getting doesn't match the result.&lt;/p&gt;

&lt;p&gt;Walking through the code and adding comments will help clarify your thoughts and give you something solid to stand on. Try it the next time you're stuck.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Use PHP-Parser to test PHP before eval()</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Mon, 27 Jan 2020 17:04:05 +0000</pubDate>
      <link>https://dev.to/nvahalik/use-php-parser-to-test-php-before-eval-4jjp</link>
      <guid>https://dev.to/nvahalik/use-php-parser-to-test-php-before-eval-4jjp</guid>
      <description>&lt;p&gt;I recently had an issue where I needed to comb through some logs and pull out some data. The data had been logged through Monolog and so it was valid PHP.&lt;/p&gt;

&lt;p&gt;Except the logs were corrupt. The logs had been written under a period of heavy load and so some of the entries were corrupt. The file "comber" couldn't detect if the output was valid. But the only way I am aware to actually turn valid PHP into a real PHP structure is &lt;code&gt;eval()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, &lt;code&gt;eval()&lt;/code&gt; presents a problem. If you pass in invalid PHP, you'll get a &lt;code&gt;ParseError&lt;/code&gt; and execution will stop.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://github.com/nikic/PHP-Parser" rel="noopener noreferrer"&gt;https://github.com/nikic/PHP-Parser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By adding this above my call to &lt;code&gt;eval()&lt;/code&gt;, I could avoid any corrupt values getting passed through:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use PhpParser\ParserFactory;

$parser = (new ParserFactory)-&amp;gt;create(ParserFactory::PREFER_PHP7)

// ...

try {
    $parser-&amp;gt;parse($buffer);
} catch (Error $error) {
    echo "Parse error: {$error-&amp;gt;getMessage()}\n";
    return;
}

eval("\$a = $buffer;");

// No more parse errors!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Worked as expected—no more &lt;code&gt;ParseError&lt;/code&gt;s were raised and the logfile combing efforts were successful.&lt;/p&gt;

</description>
      <category>php</category>
    </item>
    <item>
      <title>Unpacking my head</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Fri, 24 Jan 2020 19:42:56 +0000</pubDate>
      <link>https://dev.to/nvahalik/unpacking-my-head-1g1j</link>
      <guid>https://dev.to/nvahalik/unpacking-my-head-1g1j</guid>
      <description>&lt;p&gt;In a Cordova application it seems like pretty often our error reporting system is complaining about an HTTP Error 0. I've pretty much always seen now that HTTP 0 errors are usually related to some sort of CORS problem. Or perhaps the system couldn't connect. That is, maybe the application was trying to issue a request while there was a disconnection of the network and when it came back up, it decided to report the error that was cached.&lt;/p&gt;

&lt;p&gt;But then I came across this answer on SE: &lt;a href="https://stackoverflow.com/a/26451773/77209" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/26451773/77209&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That answer goes way more in depth into why CORS-related errors are technically &lt;code&gt;status = 0&lt;/code&gt;. It's essentially because it's a problem before the request gets made.&lt;/p&gt;

&lt;p&gt;So, I recently ran into a problem where I was &lt;em&gt;still&lt;/em&gt; seeing these HTTP 0 errors. So I decided to check on the logs while some other stuff was running. That's when I noticed something.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note: I check New Relic APM regularly. It's open in a tab and I peruse through it and have alerts set up in case something wild happens. Nothing ever was showing in this tab. At least nothing too wild. A couple of DB issues and a handful of errors with a remote web service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But back to the logs: so in the logs I checked the timestamp of the request in my reporting system with the log... and I see that the &lt;code&gt;OPTIONS&lt;/code&gt; request for the resource responded with a &lt;code&gt;502&lt;/code&gt; status code.&lt;/p&gt;

&lt;p&gt;Hmmm. Why? &lt;em&gt;Open up the error logs...&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;... [crit] 30081#30081: *4243516 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: #.#.#.#, server: redacted.server.name, request: "POST /api/v2/request-endpoint HTTP/1.1", upstream: "fastcgi://unix:/var/run/php/php7.3-fpm.sock:", host: "redacted.server.name", referrer: "http://url/"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Whoa. So the webserver lost connection to FPM. Is FPM crashing?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[24-Jan-2020 13:48:57] WARNING: [pool www] child 13296 exited on signal 15 (SIGTERM) after 259.005807 seconds from start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Well, I'm not entirely sure why at this point but given that the application infrastructure has changed recently, I decided to go back over the config files.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;php-fpm&lt;/code&gt; got bumped a little bit since some stuff was taken off this server. Also, it appears that I had been hitting the pool limits and so given the size of the box, it got a pretty sizeable bump.&lt;/p&gt;

&lt;p&gt;So after deploying that change, I started getting errors again. &lt;/p&gt;

&lt;p&gt;MySQL connection limit reached. Whoops. Upping the number of &lt;code&gt;php-fpm&lt;/code&gt; processes means that now that number needs to go up some.&lt;/p&gt;

&lt;p&gt;So after deploying that change, it really got me thinking about how easy it is to miss this stuff.&lt;/p&gt;

&lt;p&gt;Even with something like New Relic and error reporting, I was getting these weird errors from the client which told me nothing.&lt;/p&gt;

&lt;p&gt;Monitoring stuff is hard, especially if you're not even sure what you're looking for.&lt;/p&gt;

&lt;p&gt;Anyway, things have died down considerably. The number of random &lt;code&gt;status = 0&lt;/code&gt; errors is practically 0 now.&lt;/p&gt;

&lt;p&gt;I'm not sure if New Relic's infrastructure system would have caught this, but hey, they give you a 30-day free trial, so why not.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>php</category>
      <category>database</category>
    </item>
    <item>
      <title>Reasons why you can get Error 0 during an HTTP Request</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Mon, 13 Jan 2020 16:15:46 +0000</pubDate>
      <link>https://dev.to/nvahalik/reasons-why-you-can-get-error-0-during-an-http-request-2aha</link>
      <guid>https://dev.to/nvahalik/reasons-why-you-can-get-error-0-during-an-http-request-2aha</guid>
      <description>&lt;p&gt;One of the most frustrating things that can happen is you can start getting Unknown errors (Error status 0) on HTTP requests in your PWA/app.&lt;/p&gt;

&lt;p&gt;Error 0 is a weird error since effectively what's happened is that the request has failed failed. Not just failed, but failed in a way that the system doesn't know how to handle. This can happen for a number of reasons, but this is my handy checklist for trying to figure things out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CORS isn't set up properly.&lt;/strong&gt; (check the browser logs, they will tell you this)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preflight is failing.&lt;/strong&gt; Is your server handling this properly? Is it returning an error?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The response is corrupt.&lt;/strong&gt; Did you leave a debugging statement in there? Did something happen to cause HTML output when the browser is expecting JSON?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Did you exceed some sort of throttling?&lt;/strong&gt; This can cause a problem, too.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>git log --simplify-by-decoration is my new best friend</title>
      <dc:creator>Nick Vahalik</dc:creator>
      <pubDate>Tue, 31 Dec 2019 01:43:45 +0000</pubDate>
      <link>https://dev.to/nvahalik/git-log-simplify-by-decoration-is-my-new-best-friend-3dc7</link>
      <guid>https://dev.to/nvahalik/git-log-simplify-by-decoration-is-my-new-best-friend-3dc7</guid>
      <description>&lt;p&gt;In short, it's this: &lt;code&gt;git log --decorate --graph --simplify-by-decoration --oneline   [--all]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's break it down:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git log&lt;/code&gt; - Well, it shows you a historical log of commits.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--decorate&lt;/code&gt; - adds information about branches and tags to the log information. If a particular commit hash is tagged or is the latest in a branch, it will show you the name of the branch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--graph&lt;/code&gt; - Git will show you some connecting information. For example, it will show you where commits end up and where the split from other branches. Really helpful if you're trying to figure out where one branch happens to split from another branch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--simplify-by-decoration&lt;/code&gt; - Ah, this is the juicy one. Basically, adding this option &lt;strong&gt;only&lt;/strong&gt; shows commits which have a decoration on them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--oneline&lt;/code&gt; - Only show one line per commit. Essentially, show the SHA, the decorator, and the first line of the commit message.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;--all&lt;/code&gt; - Adding this last option will show you not just the active branch, but all tags and branches that Git knows about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* a471bb62 (HEAD -&amp;gt; sprint-7, tag: 2.0.0-9, origin/sprint-7) PROJ-1124 - Use the new widget.
* 39ad8aaf (PROJ-1125-no-boz-far) PROJ-1125 - Also handle no condition.
* 153feba4 (PROJ-1116-access-faz-boo) A couple of extra fixes.
| * 446ae0a4 (origin/PROJ-1125-no-boz-far) navigate to page
|/
| * a1716093 (origin/PROJ-1116-access-faz-boo) fixed access
|/
| * 1c0e6857 (origin/PROJ-1117-lorem-ipsum) fixed css
|/
| * 173131cf (refs/stash) WIP on PROJ-1117-baz-bar-foo: 266064d3 added icons
|/
| * bc7570bc (PROJ-880-bar-baz) PROJ-880 - Adding content.
|/
* bb5f437f (origin/staging, staging) PROJ-181 - Adjust spacing.
* 49f8dae4 (origin/master, origin/HEAD, master) 1.3.12
| * 8ec3b4f9 (tag: 2.0.0b3) Tagging 2.0.0-3.
| * d258e3c8 (tag: 2.0.0) 2.0.0
|/
| * 06f365ee (origin/PROJ-880-bar-baz) PROJ-880 - Adding content.
|/
* 24c6e2e0 (tag: 1.3.11) 1.3.11
&amp;lt;snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
    </item>
  </channel>
</rss>
