Here we go! Your Laravel application is now in production. You are online, ready to start making a lot of mecha-gazillion dollars.
Roses are red, violets are blue, tests are green and you feel so cool.
First user. Ten users. One hundred users. A thousand users! Suddenly, the more you go forward, the more your application becomes slower.
What should you do? Well… Devil is in details.
After some searching, I decided to create this list of 20 tips you can use to boost your Laravel application to a new level.
I will start with the basic stuff, mostly something you can apply in seconds. Then, I will introduce you to something at an intermediate level. Finally, the advanced things. If you follow the next steps in a proper way, I am sure your application will get a real boost.
Enjoy it! If you want to suggest something, leave a comment below! I will be more than happy to add it.
Basic Stuff
Ok, let’s see what we can do in seconds.
Route Caching
Every time your server execute a request, all the routes are registered. It takes some time. However, you can choose to cache them to skip the step instead of doing it.
Caching your routes is easy. All you have to do is to execute a command after you deploy your application:
1 |
php artisan route:cache |
However, if you add new routes or edit something don’t forget to clear the route cache and re-execute the command.
1 2 3 4 5 |
php artisan route:clear # and then, again... php artisan route:cache |
Note this only work with Controller routes.
Config Caching
Just like routes, you can also cache configuration files of your application.
Think about it: every single time you send a request to your app, Laravel needs to load different configuration files, and the .env file must be opened to read its contents. This approach is not performance oriented, huh?
Don’t worry; there is an Artisan command for that.
1 |
php artisan config:cache |
You can use it after your deploy. Just like routes, don’t forget to clear the config cache if you edit something.
1 2 3 4 5 |
php artisan config:clear # and then, again... php artisan config:cache |
Optimize the Composer Autoloader
Usually, the autoloader file generated by Composer is quite fast. However, in a production environment, things can get slower if PSR-4 and PSR-0 autoload rules are set up.
You can optimize the autoloader file creation process by adding the
1 |
$ composer dump-autoload -o |
command to your deploy script. Don’t forget it 😉
False Myths: “Don’t Use a Lot of Blade Views”
I have heard this a LOOOOOOT of times.
“Don’t use a lot of Blade views because application performance will decrease!”
Totally – not – right. Seriously.
Remember: Laravel compiles Blade views. Compiled means that at the end of the process you have a compiled, complete file that is going to be used instead of multiple files.
So, don’t worry 😉
Intermediate Stuff
Switch to Another/Better Cache/Session Driver
By default, when you start a new Laravel project the default driver for Cache and Sessions is “file”. It is ok for the local development environment and small projects, but wrong when things are getting serious.
So, think about moving on to something better like Redis. Laravel has built-in support for it, and all you need to do is to install Predis.
Update Laravel Version ASAP
Remember to update Laravel as soon as possible when a new version comes out. It’s not just about features: when possible, all the contributors spend time fixing performance issues around the codebase.
So, stay tuned and keep your code up to date.
Unused Service Removal
This one is a trick a lot of people often forget. The fundamental question is the old one:
“Do I need it?”
Now, we know that Laravel has a lot of stuff inside of it. After all, we are talking about a full-stack framework, made to solve a lot of specific issues. That said, the perfect weapon does not exist.
Spend some time checking the config/app.php file to see if you can find a service provider you don’t need. Try to remove it and test your application if everything is ok.
It should help (a little bit)!
Use Eager Loading for your Queries
If you know what Laravel is, you probably also know what the Eager Loading is. In case you live under a rock, Eager Loading is a way to improve Eloquent performance by using a specific syntax to reduce the number of queries it sends to the database.
This problem is known as N + 1 query problem. Let’s make an example. You have two models: Book and Author. Every book has its author.
1 2 3 4 5 |
$books = App\Book::all(); foreach ($books as $book) { echo $book->author->name; } |
Imagine that you have 1000 books in your database. Now, this code is going to execute 1001 queries to retrieve the author name for 1000 books.
1 (query to fetch data for 1000 books) + 1000 (queries to fetch author data for every book) = 1001.
However, if you write this code like
1 2 3 4 5 |
$books = App\Book::with('author')->get(); foreach ($books as $book) { echo $book->author->name; } |
the underlying query is changed to avoid this performance problem. You will execute only two queries instead of 1001! A huge performance improvement.
Cache Query Results
Sometimes, caching specific queries results can be a great idea.
Imagine this situation: you are showing a “Top 10 Albums” in the home page of your application. The job is done by a query that fetches data from the database (maybe involving artists and other tables). Your home page is seen 1000 times/hour.
1000 queries per hour for that widget. 24000 queries/day for a f*****g widget.
Now, let’s say the Top 10 is updated hourly. So, what about caching the result for an hour?
1 2 3 |
$value = Cache::remember('top_10_albums', 60, function () { return Album::with('artist', 'producer')->getTopTen(); }); |
With the remember method of the Cache component you will be able to fetch data from the database, save it on the cache storage and use it for 60 minutes. After that period, “fresh” data will be fetched again from the database.
From 24000 to 24 queries/day.
Add Indexes to your Tables
Remember to add indexes to your tables when necessary. It may seem a trivial tip, but it’s not. I saw a lot of applications without indexes on their tables.
You can do it easily with a new migration and dedicated code inside of it.
1 2 3 |
Schema::table('my_table_name', function(Blueprint $table){ $table->index('field_to_be_indexed'); }); |
You can’t just create indexes wherever you like it. You must study your business, your code and queries, understand where it is necessary and then add the required indexes.
Too Many Middlewares?
Under the hood, Laravel does a lot of (pre/post) calls for every middleware you register in the application code. So, be sure to double check them and remove what you don’t need.
You can find the list, as usual, in the Kernel.php file.
Time to Use Queues!
Sometimes, Laravel applications are way slower than expected because you can asynchronously implement synchronous tasks and you don’t do it.
The most used example is the welcome email sending. Let’s imagine a flow:
- user filled our form;
- we save his/her details on the database;
- we send him/her an email with a welcome message and a confirmation link;
- we show him/her a “Thank you!” view;
Well, a lot of times a saw all this stuff entirely done in a controller and sequentially.
My suggestion here is to learn how to use events and queues. You could use them to delegate the email sending procedure to another dedicated process and improve the final user experience.
Advanced Stuff
Use Pusher to Improve Async Tasks
Just a couple of lines above I wrote about queues. Well, you can also use queues to improve user-facing tasks, sometimes.
Imagine that you are creating a heavy (in terms of calculations) procedure and you want to show a progress bar for your user. You can easily use an async job on a queue and an integration with Pusher to send messages to your frontend even if the task is not complete yet.
Another often used example is a way to send notifications to your user without the need to reload the page.
Think about it!
Use Logs / Debugbars / Laravel Telescope to Measure Stuff
There is a quote that I love about what you can improve. I’ve heard from my CEO (thanks Massimo!) quoting Peter Drucker.
You can’t improve it if you can’t measure it.
This concept fits perfectly in the context of web applications. There is a lot of stuff to be measured to improve requests management times. Luckily for us, there are also a lot of excellent tools to do it.
- Slow query logs: in MySQL, MariaDB and other databases you can enable a dedicated log for slow queries to track what is taking a lot of time. You can use this data to understand if a specific piece of code (or query) must be changed or optimized;
- Debugbar: Laravel Debugbar is a fantastic package you can use to collect data about a lot of your application aspects. Queries, views, times and so on;
- Laravel Telescope: another cool tool is Laravel Telescope, defined as “an elegant debug assistant” for a Laravel application. If you are interested, I have already written an article about it here;
Update PHP
It is quite simple, but I decided to add it to the Advanced group, because if your codebase is a large one this could be a difficult task.
Consider updating your old application to PHP 7.* if you have not done it. Of course, don’t also forget that you will also need to update your Laravel version.
Consider Lumen for Services
If your application is growing a lot, start considering a way to split stuff into separate services. There is not a clear guide for doing this: the perfect splitting criteria depends on many factors, from the domain of your application to the work you need to do to split all the necessary stuff.
However, when you start to “break the monolith” your application can breathe again.
If you are interested in the subject, this article can be a good start: https://medium.com/@munza/large-scale-laravel-application-9d52c3d38e51
Serve Assets with a CDN
I am quite sure you have a lot of frontend assets, like CSS files and JS scripts.
You can reduce the amount of data you send to your users in many ways:
- Assets minification;
- Assets bundling;
- Enable gzip compression;
However, if you experience a lot of traffic you can host your assets to a dedicated CDN service, like:
- Akamai
- Max CDN;
- Cloudflare;
- Amazon AWS Services (S3 + CloudFront);
Use Advanced Measurement Tools
Installing the Laravel Debugbar or Telescope can be a good start, but for more significant applications, it could not be enough.
You should choose more advanced tools like:
- New Relic;
- AppOptics;
- Datadog;
- Sentry;
Applications in the list above do not do the same thing: they are made for different purposes. Spend some time studying them to understand how they can help you.
Vertical Scaling
You have done everything to optimize every single bit of your code, but your application is growing. Sooner or later you will need to scale your application vertically.
It’s a simple concept: more RAM, more space, more bandwidth, more CPU.
Note that this is a quite common practice for a lot of startups that do not have enough time to schedule a refactoring/optimization procedure. It’s perfectly ok, so consider it like a temporary solution to give you some breath.
Horizontal Scaling
Another approach to scaling, different from the traditional vertical scaling, for two reasons:
- Instead of raising your resources on the current configuration, you will add more items to it to deal with the traffic increase differently. While in the context of vertical scaling you merely raise the level of your server configuration, scaling your application horizontally means that it will run on different machines, maybe behind a load balancer and other services. It means more setup and configuration; it’s not so simple as it may seem;
- Not all applications are written to be scaled horizontally in a matter of hours. Sometimes you will need to refactor and isolate some code; sometimes you will need to split your application in different, smaller services that are going to scale differently;
There is a lot of work to do, but when you can horizontally scale your application, the benefits are incredible.
Conclusions
Enough stuff for today! I have created this list by merging my personal experiences and some research I have done for it.
If you want, feel free to suggest new items, I will be happy to update it accordingly.