By continuing your visit to this site, you accept the use of cookies. Read more

Scout APM helps PHP developers pinpoint N+1 queries, memory leaks & more so you can troubleshoot fast & get back to coding faster. Start your free 14-day trial today .

« back — written by Brent on May 30, 2019

A project at Spatie

The month of May marks the first year anniversary of a client project I've been working on at Spatie. I thought it useful to share some statistics with the community, and give you a feeling of what a "real life web project" might look like.

Let's start with a general overview. The project, a web application, features an admin interface to manage inventories, contacts and contracts; bookings, automatic invoicing and about ten third party integrations.

In the future we'll be exposing several of these features to the outside via an API, its main goal to power a mobile app for clients of the platform. The admin panel is already in use in production.

The project is built in Laravel, a PHP framework. We use Blade as a templating engine in combination with many VueJS components. Tailwind is the CSS framework used.

# Some numbers

So, how much code have we written the past year? Here's a summary, gathered with the phploc package:

2,062 files

files 126,736 lines of code

lines of code 97,423 logical lines of code

Let's zoom into statistics about the backend code, my area:

1,086 classes; 32 interfaces; 28 traits

classes; interfaces; traits Average LLOC per class: 8

per class: Maximum LLOC per class: 139

per class: 3,245 public methods

The amount of lines split per file type looks like this:

Let's further dive into how the backend code is structured, by using Stefan's laravel-stats package.

To start with, I should explain something about our big Laravel projects. Instead of using the default Laravel project structure, our code is split into two namespaces: "application code" and "domain code".

Domain code holds all business logic and is used by the application layer. If you want to dive further into this topic, you can read about it here.

The following graph shows how application and domain code relate to each other:

By splitting business and application code, we're able to provide a flexible, maintainable and highly testable core. Application code makes use of this core and looks very much like your average Laravel project.

The bulk of our domain code consists of three types of classes:

Models — 80 classes

classes Actions — 205 classes

classes DTO s — 63 classes

While the application layer mostly consists of:

Controllers — 130 classes and 309 routes

classes and routes ViewModels — 82 classes

classes Blade views — 313 files; these are not included in the chart above

Because of the lifecycle of the project, there's room for improvement. For example, we're not using DTO s everywhere, as they were added at a later time.

As with all things: we learn as we go. After a year, it's normal that some parts of the code can be considered "old". We have a rule that states that when we work in these old parts of the codebase, we refactor them.

A big advantage of moving code into domains is testability. While our domain code is heavily unit tested, our application code is mostly only integration tested. In our experience, it's a workable balance between highly tested code and being able to meet deadlines.

At the moment we have 840 tests doing 1,728 assertions. Our test suite could always be improved, but I am very confident deploying new features and refactors without the fear of breaking stuff — thanks to our test suite.

# Code structure

I'm a big proponent of clean code. We try to keep our code clean and maintainable, by setting a few rules of thumb:

Classes should be small, 50 lines of code should be the maximum

Methods should also be small and easy to reason about

We prefer clear names over short and cryptic names

You probably noticed that we don't always keep these rules. There are some classes that are longer and more complex.

These classes are the result of making choices: sometimes some technical debt is allowed to meet deadlines — as long as we're aware of it.

I've made a little tool in the past which I use to generate "heat maps" of the codebase. It will take all code in a folder, and generate an image by overlaying the code structure on top of it.

I can use this tool to locate large files, and refactor them when there's time. We have done this in the past, and it works very well.

Here's part of this image of a subdomain in our project:

The darker the image, the more code across all files in that position. You can see that while some files are longer, most of the code lives in the upper 50 lines, something we strive for.

We ensure these short classes and consistent code by using a few tools and methods:

Internal PR s and code reviews; despite what you might think, this saves time

s and code reviews; despite what you might think, this saves time We use static analysis, more specifically PhpStan; to prevent subtle bugs

We use PHP CS fixer to ensure consistent code style

Like I said before: I'm a firm proponent of clean code. When you're working with several people in the same codebase, it's a must to keep your code clean and clear, to secure its future.

# In closing

Finally, I'd like to show the GIT history of the project visualised with Gource. We've been working on this project with, in total, 7 contributors, and now have more than 4,000 commits listed.

You can clearly see the different "branches" I talked about earlier: application- and domain code; but this overview also includes Blade, JavaScript and CSS files.

So what about your projects? Are you able to share your own stats? Feel free to send me a tweet or an email!