Introduction
Laravel already gives for web project several command surfaces: Artisan, Composer scripts, npm scripts, and even a small admin-facing interface for a restricted set of Artisan commands. So the obvious question is why this repository still keeps a top-level Makefile.
During development, many other commands are still needed, either as commands from the terminal or in the form of bash (zsh) scripts. The sum of all such commands and their structure brings problems:
- with remembering their definition
- with the length of the command line (requiring many keystrokes on the keyboard)
- with executing the command in the right place (directory) in the application structure A very practical solution is to use "make" as a shell language to support development acting as a thin coordination layer over the commands the developer clearly runs often: route inspection, cache resets, log cleanup, grouped test execution, static analysis, and a few environment-specific checks.
It means the Makefile is the approved command surface for repetitive development work inside the editor.
This article walks through what the current Makefile actually does, why these targets exist, and what the file says about how this Laravel project is operated.
So the Makefile is not just convenience glue. It expresses which operations are considered normal for daily development, and which stay outside that surface.
Practical example of a Makefile used for development
The described Makefile is a shortened example of the most commonly used commands when developing Laravel applications using Docker or VirtualBox on Windows 11. The Makefile below is used in the Debian 13 environment for different workspaces (development, preparation for deployment, staging). For this reason, shared files tools.mk (common commands for all workspaces) and tools-special.mk are used.
Makefile for development workspace
## Makefile for usefull scripts in Laravel11 Blog project ## cmd: make .PHONY: help copy test module CURRENT=$(shell date +'_%g%m%d') export STAMP:=$(CURRENT) # Include common targets from tools*.mk include /var/www/tools.mk include /var/www/tools-special.mk checklinks: ## check for broken links in the project (install broken-link-checker globally with npm install -g broken-link-checker) npx broken-link-checker http://192.168.0.175:81 --recursive --ordered --filter-level 2 list: ## show routes (all - no param | filter - add param: name="crop") @if [ -n "$(name)" ]; then \ php artisan route:list --name "$(name)"; \ else \ php artisan route:list; \ fi listv: ## show all routes incl. middleware (all - no param | filter - add param: name="crop") @if [ -n "$(name)" ]; then \ php artisan route:list --name "$(name)" --verbose; \ else \ php artisan route:list --verbose; \ fi rlog: ## reset laravel log to empty content truncate -s 0 storage/logs/laravel.log rc: ## reset all laravel caches php artisan optimize:clear run: ## run phpunit tests for group run vendor/bin/phpunit --group run --testdox runz: ## run phpunit tests for group runz (all - no param | filter - add param e.g. filter="unzip") @if [ -n "$(filter)" ]; then \ vendor/bin/phpunit --group runz --filter "$(filter)" --testdox; \ else \ vendor/bin/phpunit --group runz --testdox; \ fi stan: ## run phpstan ./vendor/bin/phpstan analyse
tools.mk
## Unified Makefile — Common targets Generated: 2026-04-03
.PHONY: help chown rc list listv run runz testm scomposer crm cpkg vscext stan
CURRENT=$(shell date +'_%g%m%d')
export STAMP:=$(CURRENT)
help: ## Tools makefile targets HELP
@echo ""
@echo "Makefile for useful scripts in Laravel11 Blog project"
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n\nTargets:\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
---: ## COMMON TARGETS
chown: ## set all project files for owner 'www-data:www-data' with 755
chown -R www-data:www-data .
chmod -R 755 .
chownacl: ## set all project files for owner 'www-data:www-data' with ACL
setfacl -R -m u:www-data:rwx .
setfacl -R -m d:u:www-data:rwx .
setfacl -R -m u:vacal:rwx .
setfacl -R -m d:u:vacal:rwx .
size: ## show disk usage of current folder in human readable format with depth 1
du -ha -d 1 | sort -h
tools-special.mk
## Unified Makefile — Special targets (unique usage) ## Generated: 2026-04-04 ---: ## SPECIAL TARGETS (development) websnap: ## create web snapshot with mobile-check.mjs script node tests/web/scripts/mobile-check.mjs webqa: ## run mobile-qa-report.mjs tests (Mobile QA Checklist) node tests/web/scripts/mobile-qa-report.mjs webtap: ## run small tap target tests with check-tap-targets.js node tests/web/check-tap-targets.js
That structure tells you several things immediately:
- Only application-specific commands stay in the local Makefile.
- The file is designed to be read by humans, not just executed by CI.
The shared include layer is important here. The loaded common makefile provides the help target, so plain make functions more like an operational menu than a build step.
Here is terminal output for command make:
Makefile for useful scripts in Laravel11 Blog project Usage: make <target> Targets: checklinks check for broken links in the project (install broken-link-checker globally with npm install -g broken-link-checker) list show routes (all - no param | filter - add param: name="crop") listv show all routes incl. middleware (all - no param | filter - add param: name="crop") rlog reset laravel log to empty content rc reset all laravel caches run run phpunit tests for group run runz run phpunit tests for group runz (all - no param | filter - add param e.g. filter="unzip") stan run phpstan testm create module (Feature) test with test="<TestName>" module="<ModuleName>" help Tools makefile targets HELP --- COMMON TARGETS chown set all project files for owner 'www-data:www-data' with 755 chownacl set all project files for owner 'www-data:www-data' with ACL size show disk usage of current folder in human readable format with depth 1 vscext create vscode-extensions.txt file with all installed extensions --- SPECIAL TARGETS (development) scomposer run composer as www-data user crm remove existing package from composer cpkg copy vendor package importobsidian to /packages/jotcomp websnap create web snapshot with mobile-check.mjs script webqa run mobile-qa-report.mjs tests (Mobile QA Checklist) webtap run small tap target tests with check-tap-targets.js
Remarks to Testing Commands
The most revealing part of the Makefile is the testing split:
run: ## run phpunit tests for group run vendor/bin/phpunit --group run --testdox runz: ## run phpunit tests for group runz (all - no param | filter - add param e.g. filter="unzip") @if [ -n "$(filter)" ]; then \ vendor/bin/phpunit --group runz --filter "$(filter)" --testdox; \ else \ vendor/bin/phpunit --group runz --testdox; \ fi
This is not generic Laravel practice. It is repository-specific and based on PHPUnit group attributes.
The existing suite contains many tests marked with Group('run') across controllers, services, models, security, and ZIP-import-related flows. That makes make run the stable test lane (for all approved tests).
The runz lane appears in repository prompt guidance for new feature developemnt work. New or modified tests are expected to carry Group('runz') so they can be validated quickly with make runz during active development. At the moment, the repository visibly contains many run tests but no current runz annotations in the inspected test tree. That suggests runz is intended as a fast-moving work-in-progress lane rather than a permanently populated bucket.
The two-lane setup gives the maintainer both a stable baseline and a place for focused current-work verification.
Summary
This project's Makefile is not an alternative interface to Laravel. It is a small operational index for the commands that matter most in this specific repository.
Its value comes from four things:
- It standardizes the commands the project actually uses.
- It encodes repository policy around testing and editor-safe workflows.
- It reflects the real shape of the application: admin-heavy, package-extended, and operationally maintained.
- It accepts environment-specific assumptions instead of hiding them behind fake portability.
A useful Makefile does not need to be universal. It needs to reduce friction in the environment where the work actually happens. In this repository, that is exactly what it does.