10 Things I Look for When I Audit Laravel Codebases

January 6, 2023

Oftentimes one of the first steps along my journey of working with a new company involves some kind of review of their code in the form of a code audit. How long the process takes depends on how much code they have and what I find. A longer audit should be more thorough, but a shorter audit should still be helpful. I put together this list of 10 things I would typically look for in an initial 2-3 hour audit of a Laravel project.

In this particular scenario I don't assume that I have any kind of access to any of the company's environments or the ability to consult with the company's past or present developers. All I have is the code they've given me.

The goal of the audit is not to point fingers or tell people what they've done wrong, but to familiarize myself with the way they do things and to better understand the state their codebase is in. Once I have completed this process I can better advise them of potential improvements that could be made. The audit doesn't necessitate setting up a dev environment. Although that may be helpful, the code should speak for itself. Later, as a follow up, I can set up a dev environment to test my assumptions and determine whether the code actually does what it appears to do.

1) Version Control

One of the first things I look at when examining a company's codebase is whether or not they are using version control. If they send me a copy of their code via email or a file sharing program, I already know that the answer is probably no. Without version control I don't know who made what changes when and I don't have a good way to allow multiple developers to work on the project at the same time. Ideally the client would have their project on Github because this is what I am most familiar with, but other solutions like Bitbucket and Gitlab would suffice. Version control also opens up the door to continuous integration and delivery which are both good strategies for an organization to adopt.

2) Updated README

As a developer, the README file is where I go first when I want to know about a project on Github. It's here that I look for instructions about installing the project in a local dev environment, what kind of requirements it may have, what the code does at a high level, and maybe some examples of how it is used. If the project has documentation, I would expect to find a link to that documentation here. However, if the default Laravel README is present, that's not a good sign. It means the developer hasn't taken the time to document their code in the most obvious and accessible place to do so. It may also mean that I am on my own when it comes to discovering any caveats involved in setting up my own development environment.

3) Laravel Version

The composer.json and composer.lock files contain information about the project's core dependencies. Once I've confirmed that these files exist, I check which version of Laravel the project is using. If I see that it's using an older version of Laravel such as version 5.6 which was released in 2018, I might ask why they've avoided upgrading. If a major version upgrade is too much, why not at least upgrade to the lastest minor version?

Having the latest version makes it easier to work with other software packages and take advantage of new features which save developers time and improve performance. If the company is not able or willing to make the investment to upgrade to the latest major version, they should at least keep up with the latest minor version which will occasionally eliminate newly-discovered security vulnerabilities and provide minor improvements. They should also be aware of when their version reaches end-of-life and will no longer be officially supported with security fixes.

4) Routes and Controllers

In the fourth step I look at the routes in the routes directory. I want to see that routes are using middleware, groups, and controllers appropriately. This also gives me a feel for the scope of the project. Here I should be able to see every URL accessible by a user and every API endpoint.

5) Database Interactions

I then will look for instances where the code interacts with the database to perform a select, insert, update, or delete operation. Ideally most queries and commands would be done through the appropriate model using Eloquent instead of the the database facade (DB) and the query builder. The models should contain fillable fields and methods relating them to other models.

I also look for queries that are manually constructed from strings which may introduce opportunities for SQL injection which could compromise the database. I don't analyze every query individually, but if I see an obvious "N+1" problem (poorly optimized queries that lead to an excessive number of requests to the database) I will make a note of it.

6) Migrations

For the sixth step in my audit I check to see that they are using migrations. Migrations are a valuable tool included with Laravel and allow the developer to codify schema changes and reliably apply them in production and development environments. Since each migration is a file with a timestamp in the filename it's easy to see when the first and last migrations were created. If they are using migrations, then I check the tables in the migrations against their models. Are there any models without tables? Are there any tables without models?

I also take this opportunity to examine the structure of individual tables including column data types. I look for a consistent naming convention for both models and tables - one that takes advantage of Laravel's automatic model-to-table mapping feature is preferrable. If you have, for example, a customer_orders table, the corresponding model would be CustomerOrder.

7) Database Seeding

I like to see that the project provides a way for a developer to seed a new database for development or testing purposes. This can be a valuable resource and provides a much safer way of populating the database than importing production data. Laravel provides ways to seed database via Database Seeders and Factories. Seeders can also be used when setting up the initial production environment database and can indicate what data is required as a minimum for the app to run.

8) Views

For this step I want to see how the frontend is rendered. This can vary widely between projects. If the project is using Blade templates, I look to see if any templates include logic that they shouldn't and whether they are using layouts. I'll scan the package.json file for anything interesting.

I mainly want to know how the frontend is connected to the backend. Is the app rendered server-side or client-side? If they're using Vue or Inertia, I might spend more time here.

9) Tests

I don't go into depth here I just want to see if there are tests and what testing library they are using. I might examine a few of the tests to see what the assertions are and get a feel for the level of coverage.

10) Readability and Maintainability

The last thing I do is look for examples of changes I could make to improve readability and maintainability. I do this by scanning through PHP files where I'm most likely to find business logic and database interactions. I want to find examples of code that is difficult to understand and repetitive. Some things I look for include variable and function names that are ambiguous or misleading, methods that are never called, and snippets of code that appear multiple times as if they were copied and pasted. I may also look for inconsistencies in syntax and use of indentation. From this I can kind of guess which if any coding standards the developers adhere to.

Post-Audit Follow Up

After performing this type of audit I should have enough information to take back to the company to advise them on what my next steps would be depending on their goals. The next logical step may involve setting up my own dev environment and testing the core features of the app as a user.

With my environment set up I should be able to validate migrations and run tests. I could also go through my notes from the first audit and test any assumptions I had.