Laravel Codebase Maintenance: Best Practices
Keeping your codebase up to date is essential, but with busy schedules and project deadlines, it can feel like a low-priority task. Often, developers get stuck using outdated versions or maintaining legacy systems, making updates seem like an overwhelming challenge.
Neglecting updates doesn't just deprive you of powerful new features—it can expose your project to security risks and make future upgrades far more complex and stressful than they need to be.
In this article, we'll explore practical strategies to ensure your Laravel app stays current. With a few proactive steps, updating doesn't have to be a hassle.
Table of Contents
- Causes of Outdated Code
- Recommendations for Keeping Your Codebase Up to Date
- 1. Leverage Dependabot for Automated Updates
- 2. Automatically Merge Pull Requests from Dependabot
- 3. Cases When You Shouldn't Automatically Merge Pull Requests
- 4. Use Version Constraints Wisely in PHP
- 5. Manage JavaScript Dependencies
- 6. Block Vulnerable Dependencies
- 7. Maintain and Automate a Robust Test Suite
- 8. Validating Your composer.json for Easier Maintainability and Updates
- Conclusion
Causes of Outdated Code
Before diving into the tips, it's important to understand why codebases become outdated:
- Time Constraints: Deadlines and deliverables take precedence, pushing updates down the priority list.
- Fear of Breaking Things: The "if it ain't broke, don't fix it" mentality leads to complacency.
- Lack of Automation: Without automated updates, it's easy to forget or postpone them.
- Dependency Conflicts: One outdated package can prevent updating others, creating a chain reaction.
- Manual Processes: Relying on manual updates increases the chance of missing critical updates.
Understanding these causes helps in crafting strategies to counteract them.
Recommendations for Keeping Your Codebase Up to Date
Here are eight essential tips to help you maintain a current and maintainable codebase.
1. Leverage Dependabot for Automated Updates
Automating updates is a key step toward maintainable code. Dependabot is a GitHub tool that automatically checks for outdated dependencies and opens pull requests to update them. It's free and easy to set up.
Create a configuration file by adding a .github/dependabot.yml
file to your repository and configure it.
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
day: wednesday
time: "06:00" # 08:00 AM in Oslo (CET/CEST), adjusted for UTC
- package-ecosystem: "composer"
directory: "/"
schedule:
interval: "weekly"
day: wednesday
time: "06:00"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
day: wednesday
time: "06:00"
This setup tells Dependabot to check for updates to your Github Actions workflows, Composer (PHP) and npm (JavaScript) dependencies every week on wednesdays at 06:00 in the morning.
2. Automatically Merge Pull Requests from Dependabot
To further automate the process, you can configure Dependabot to automatically merge minor and patch updates. To enable Auto-Merge you should add or update your dependabot.yml
with:
name: "Dependabot auto-merge"
on: pull_request_target
permissions:
pull-requests: write
contents: write
jobs:
dependabot:
runs-on: ubuntu-latest
timeout-minutes: 5
if: ${{ github.actor == 'dependabot[bot]' }}
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@v2.2.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Auto-merge Dependabot PRs for semver-minor updates
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Auto-merge Dependabot PRs for semver-patch updates
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
In the example above you'll need to add a GITHUB_TOKEN
to your repository secrets. This token is used to authenticate Github CLI / gh
that is used to merge the pull requests.
Pros:
- Saves time by reducing manual intervention.
- Keeps dependencies up to date regularly.
Cons:
- Risk of introducing breaking changes if test-suite is not comprehensive.
- May not align with strict code review policies.
It's important to have up-to-date tests and that all checks pass before merging. You should also be careful with automatically deploying these changes to production without human oversight.
3. Cases When You Shouldn't Automatically Merge Pull Requests
Automatic merging isn't always the best choice. Here are some situations where you might want to avoid auto-merge:
- Strict Policies: Organizations that require human code reviews.
- Critical Applications: Projects where stability is of the highest importance.
- Complex Dependencies: Updates that could introduce significant changes requiring manual testing.
In these scenarios, it's better to review and merge updates manually.
4. Use Version Constraints Wisely in PHP
Setting appropriate version constraints in your composer.json
ensures you receive updates without unexpected breaking changes. Example of Version Constraints:
{
"require": {
"php": "^8.3",
"laravel/framework": "^11.23.2",
"laravel/tinker": "^2.9.0"
}
}
The caret (^) allows updates that do not change the leftmost non-zero digit. In this case, for laravel/framework
it allows any version from 11.0 up to but not including 12.0.
Another command that you should use regularly is the composer bump
command. It updates all dependencies and adjusts the version numbers in your composer.json
. This simplifies the process of keeping your PHP dependencies current. And it allows you to easily see which versions you're using.
composer bump
5. Manage JavaScript Dependencies
Keeping your frontend dependencies up to date is equally important. Set Version Constraints in package.json
. Use the caret (^) or tilde (~) to define acceptable version ranges.
{
"dependencies": {
"prettier": "^3.3.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"vite": "^5.4.8"
}
}
Also with npm you can use a tool to update the versions in package.json
, similar to composer bump
. Updating Dependencies with npm-check-updates:
- Install npm-check-updates:
npm install -g npm-check-updates
- Update package.json:
ncu -u
- Install Updated Packages:
npm install
6. Block Vulnerable Dependencies
The team at Roave has created the package Security Advisories which prevents the installation of Composer dependencies with known security vulnerabilities. Instead of reacting after you've installed vulnerable packages, this tool stops them from being added in the first place.
To get started, simply add it as a development dependency:
composer require --dev roave/security-advisories:dev-latest
7. Maintain and Automate a Robust Test Suite
A strong test suite is your safety net when updating dependencies.
- Write Comprehensive Tests: Cover critical paths and functionalities.
- Automate Testing: Integrate with CI/CD pipelines like GitHub Actions.
- Require Passing Tests: Configure your repository to prevent merging if tests fail.
Automate your test workflow to ensure updates are tested efficiently. Create a Github Actions workflow at .github/workflows/phpunit.yml
:
name: Run PHPUnit
on: [push, pull_request]
jobs:
phpunit:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Get Composer Cache Directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
tools: composer:v2
coverage: none
- name: Install Composer dependencies
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --optimize-autoloader
- name: Test with phpunit
run: php artisan test
If any of your current dependencies have known vulnerabilities, the installation will fail, and Composer will show you the specific problematic packages. Going forward, when you run composer require or composer update, Roave Security Advisories will block the installation of any vulnerable packages, ensuring your project stays secure.
8. Validating Your composer.json for Easier Maintainability and Updates
One powerful, but often overlooked, step in keeping your Laravel application maintainable and up to date is validating your composer.json
file. Running validation checks ensures your dependency configuration file is syntactically correct.
Why Composer Validation is Important
- Avoid Broken Dependency Files: A malformed or misconfigured composer.json can prevent your dependencies from installing correctly. Validating the file ensures that everything is configured properly before installation.
- Strict Adherence to Standards: The composer validate --strict command checks for not just basic syntax errors but also enforces adherence to best practices, like having required fields such as name, description, and license.
- Easier Troubleshooting: Composer validation can catch issues early on, preventing them from cascading into bigger problems down the line. This makes future debugging and updates easier.
Here is a Github Actions workflow that validates your composer.json
file:
name: Run composer validate
on: [push, pull_request]
jobs:
composer-validate:
name: Validate composer.json
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get Composer Cache Directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
tools: composer:v2
coverage: none
- name: Validate composer.json
run: composer validate --strict
- name: Install Composer dependencies
run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
- name: Check namespaces
run: composer dump-autoload --optimize --strict-psr
Conclusion
Keeping your Laravel codebase up to date is crucial for security, performance, and maintainability. Regular updates help you leverage new features, improve performance, and reduce technical debt.
By implementing the tips in this article, you're doing your future self and your clients a favor. You'll be setting the stage for a robust, easily maintainable Laravel application that stands the test of time. In the long run, that's what truly matters.
If you're looking to learn more about keeping your codebase in top shape, consider subscribing to my newsletter. I share weekly articles packed with developer tips and best practices. Here are some resources that you might find useful:
- YouTube - Composer validity with GitHub Actions - Professional PHP development in 2024 by Frank Prins.
- Preventing Installing Composer Dependencies with Known Security Vulnerabilities by Ash Allen.
Happy coding, and may your projects always be up to date!