[MORSALIN]
SamplesFictional client · real format

See what you get before you pay.

One invented project, followed through three documents: the report from a Codebase Assessment, the fixed-price proposal that came out of it, and an update from the middle of the work.

Fictional example. Northwind Bookings is an invented company, and every name, file path, number and finding here is made up to show the format and level of detail you get. Prices are current list prices.

Codebase Assessment: Northwind Bookings

Client
Northwind Bookings (fictional)
App
Appointment-booking SaaS for small service businesses
Stack
Laravel 10.48, PHP 8.1, Vue 3, MySQL 8.0, Laravel Nova 4 admin
Size
About 40k lines in one repository (PHP, Vue, Blade)
Hosting
One VPS running Ubuntu 22.04: nginx, PHP-FPM, MySQL and the queue worker
Review
Codebase Assessment, Standard (code plus hosting and deploy)
Access used
Read-only repository and server access. No production data copied.
Delivered
Fri 9 Oct 2026

Summary

Northwind Bookings works and is worth keeping. The code follows normal Laravel conventions, and a new developer could find their way around it in days, not weeks. It is not safe to leave as it is, though. Three problems need attention now. Live payment and email passwords are still readable in the code history. Any logged-in customer can cancel another customer’s booking. And the nightly backups sit on the same server they protect and have never been restored. Beyond those three, the app runs on PHP and Laravel versions that no longer get security fixes, very little of the booking flow is covered by tests, and nothing alerts anyone when the app or its reminder queue stops. None of this calls for a rebuild. I recommend fixing it in three phases: urgent risks first, then an upgrade to supported versions, then tests and monitoring. Each phase has a fixed price below.

Scorecard

SecurityAt riskSecrets in git history, a missing ownership check on cancel, and open mass assignment on booking updates.
Versions and dependenciesAt riskPHP 8.1 and Laravel 10 no longer get security fixes. composer audit lists open advisories.
Data safetyAt riskBackups run nightly but stay on the same disk and have never been restored.
TestsNeeds work12% coverage on the booking flow and 4 tests failing since February 2025.
Deploy and operationsNeeds workManual deploys from a laptop, no CI, an unsupervised queue worker and no alerts.
PerformanceFairFine for current traffic. The admin bookings list and the availability check slow down as data grows.
Code structureGoodThin controllers, service classes for availability and payments, and consistent naming. Easy to hand over.
DocumentationFairThe README gets a local copy running. There is no deploy or recovery runbook.

Findings, most urgent first

F01CriticalSecurityEffort: Small

Live secrets are still in the git history

Evidence
gitleaks finds a .env.production file added in commit 3f9c2ab (Mar 2023) and deleted in 7d01e4c. It still holds the live Stripe secret key, the SMTP password and APP_KEY. config/services.php:31 also hardcodes the SMS API token as an env() fallback. I list locations only; no values are copied into this report.
Impact
Anyone who has ever cloned the repository, including former developers and contractors, can take payments, send email as you and decrypt customer phone numbers.
Fix
Rotate the Stripe, SMTP and SMS credentials now. Rotate APP_KEY carefully: customers.phone uses an encrypted cast, so rotate after the Laravel 11 step using APP_PREVIOUS_KEYS, or re-encrypt with a one-off script first. Remove the hardcoded fallback and add a gitleaks check to CI.
F02CriticalSecurityEffort: Small

Any logged-in customer can cancel any booking

Evidence
routes/api.php:41 maps POST /api/bookings/{booking}/cancel to BookingController::cancel. The route has the auth middleware, but BookingController.php:132 never checks that the booking belongs to the user, and there is no BookingPolicy. Booking IDs are sequential.
Impact
A customer, or a script with one customer account, can cancel other businesses’ appointments by counting through IDs.
Fix
Add a BookingPolicy covering view, update and cancel, and call it from every booking route. Add feature tests that prove a second user gets a 403.
F03CriticalData safetyEffort: Small

Backups stay on the same server and have never been restored

Evidence
/etc/cron.d/nwb-backup runs mysqldump nightly into /var/backups/mysql on the same VPS, without --single-transaction. Nothing is copied offsite, uploaded files in storage/app/public are not backed up, and no restore is recorded anywhere.
Impact
If the VPS disk fails or the provider account is lost, every booking and customer record goes with it. The dumps may also be inconsistent, because they are taken while bookings are being written.
Fix
Encrypted offsite backups of the database and uploads, a consistent dump, a restore drill on a spare server, and a one-page runbook.
F04HighVersionsEffort: Large

PHP 8.1 and Laravel 10 no longer get security fixes

Evidence
php -v on the server reports 8.1. composer.json requires laravel/framework ^10.48. PHP 8.1 security support ended 31 Dec 2025, and Laravel 10 security fixes ended 4 Feb 2025. MySQL 8.0 also reached end of life on 30 Apr 2026, which is lower urgency for a database that isn’t exposed to the internet.
Impact
New vulnerabilities in PHP or Laravel will not be patched. Customer security questionnaires will flag the versions, and newer packages already refuse to install.
Fix
Upgrade Laravel 10 → 11 → 12 → 13, one version at a time, and move PHP to 8.4, installed side by side so rollback is quick. Upgrade MySQL to 8.4 LTS later, as a separate step.
F05HighDependenciesEffort: Medium

Open security advisories in PHP and JavaScript packages

Evidence
composer audit reports 5 advisories in 3 packages, 2 rated high, including the PDF invoice package and the HTTP client used for SMS reminders. npm audit reports 2 high advisories, both in build-time tooling. The iCal export package has had no release since 2022. The Nova 4 license expired in June 2026.
Impact
Some advisories are reachable from public pages. The expired Nova license blocks the Nova 5 download that Laravel 12 and 13 need.
Fix
Update packages during the upgrade and replace the iCal package with a small in-app class. Renew Nova before the upgrade starts.
F06HighSecurityEffort: Small

Booking updates accept any field (mass assignment)

Evidence
app/Models/Booking.php:14 sets $guarded = []. app/Http/Controllers/BookingController.php:88 calls $booking->update($request->all()).
Impact
A customer rescheduling a booking can also send price_cents, status or business_id and have them saved. That means free bookings, or a booking moved into another business.
Fix
Add an UpdateBookingRequest with explicit rules, use $request->validated(), and replace $guarded with a $fillable list. Add a test that extra fields are ignored.
F07HighOperationsEffort: Small

The queue worker isn’t supervised

Evidence
deploy.sh:22 starts the worker with nohup php artisan queue:work &. There is no systemd or Supervisor unit. storage/logs shows no worker output from 14 to 16 Aug 2026, after a provider reboot, and 1,284 reminder jobs were sent late when someone restarted it by hand.
Impact
Reminder emails and SMS stop silently after any reboot or crash. Missed reminders mean no-shows for your customers.
Fix
A systemd unit with automatic restart, php artisan queue:restart in the deploy, and a heartbeat that alerts when the queue stops moving.
F08HighTestsEffort: Medium

The booking flow is 12% covered, and 4 tests fail

Evidence
phpunit --coverage-text: 31 tests, 4 failing in tests/Feature/ReminderTest.php (broken since Feb 2025). app/Services/AvailabilityService.php has 0% coverage and BookingController.php has 9%. Nothing tests double-booking or time zones.
Impact
Every change, the upgrade included, is checked by hand or not at all. Double-booking bugs would reach customers first.
Fix
Before the upgrade, add smoke tests for signup, login, book, reschedule, cancel and deposit payment. Afterward, add fuller tests for availability and double-booking.
F09MediumPerformanceEffort: Small

N+1 queries on the admin bookings list

Evidence
app/Http/Controllers/Admin/BookingIndexController.php:37 loads bookings without eager loading. BookingResource then reads customer, service and staff for each row. On a staging copy, one page of 50 rows runs 152 queries and takes about 1.9 s.
Impact
The page your customers’ staff use most gets slower every month as bookings grow.
Fix
Eager-load the relations with with(), select only the needed columns, and add a test that fails if the query count goes above a set limit.
F10MediumPerformanceEffort: Small

Missing index for the availability check

Evidence
database/migrations/2022_05_03_120000_create_bookings_table.php indexes business_id alone. AvailabilityService.php:61 filters on business_id, staff_id and starts_at. EXPLAIN shows a scan of about 410k rows for the largest business.
Impact
Booking pages slow down for your biggest customers first, and the slow query holds locks while others are booking.
Fix
Add a composite index on (business_id, staff_id, starts_at) with an online migration, then check the query plan again.
F11MediumDeliveryEffort: Small

No CI; deploys run by hand from a laptop

Evidence
There is no .github/workflows directory. deploy.sh runs git pull, composer install and php artisan migrate --force on the server with no test run and no backup first. The last three deploys came from two different laptops.
Impact
Broken code can reach production without anyone running the tests, and a failed migration has no rollback.
Fix
Run tests and a build on every push, and make deploys run only after the tests pass. Take a database dump before migrations.
F12MediumOperationsEffort: Small

No monitoring, and the error log is never rotated

Evidence
No uptime check, error tracking or disk alert exists. LOG_CHANNEL=single, and storage/logs/laravel.log is 1.3 GB. The disk is 81% full.
Impact
You hear about outages from customers. A full disk would stop MySQL and the app together.
Fix
Uptime and certificate checks, a queue heartbeat, disk and memory alerts sent to you first, and the daily log channel with retention.

Recommended plan, fixed prices

Phase 1: Close the urgent risks (start now, 1–2 weeks)

$1,040
  • Remediation Sprint, Priority fixes: F01 secret rotation, F02 booking policy, F06 mass assignment, F07 supervised queue worker ($650)
  • Backup & Disaster Recovery, Starter: F03 encrypted offsite backups, one restore drill, runbook ($390)

Phase 2: Move to supported versions (3–4 weeks)

$1,950, less the $910 credit: $1,040 due
  • Laravel Upgrade, Plus: 10 → 11 → 12 → 13 with PHP 8.4 and smoke tests on the booking flow (F04, F05, F08) ($1,560)
  • CI/CD Pipeline, Starter: F11 tests and build on every push ($390)
  • The $910 assessment fee is credited here if this phase is approved by 8 Nov 2026. That leaves $1,040 to pay.

Phase 3: Keep it healthy (when it suits your budget)

$2,080, or any item on its own
  • Test Coverage for Critical Flows, Starter: availability, double-booking and deposit payment in CI ($780)
  • Monitoring & Alerting, Starter: F12 uptime, heartbeats, server alerts, runbook ($260)
  • Remediation Sprint, Priority fixes: F09 N+1 queries, F10 availability index ($650)
  • End-of-Life Runtime Upgrade, Starter: MySQL 8.0 → 8.4 LTS ($390)

After handover (optional)

$455/mo
  • Monthly Care Plan, Standard: patches, a monthly restore check, a short report and 7 hours a month (2 routine + 5 change), with same-working-day replies. Minimum 3 months.

Next steps

  • Read the summary and the three Critical findings. Rotating the secrets in F01 is worth doing today, whatever you decide about the rest.
  • Send questions by email. Follow-up is included until Fri 16 Oct, plus one optional 30-minute call.
  • Tell me which phases you want. A fixed proposal for Phase 2 is attached, and I can send one for Phase 1 the same day.
  • Renew the Nova license before the upgrade starts (F05).
  • Share this report with any developer you hire. It’s yours.

Want this for your app?

The Codebase Assessment is a fixed-price first step, and its fee is credited toward the work that follows.

Codebase Assessment