The Technical Debt of Laravel and how you should avoid them

Elson Tan
5 min readSep 26, 2019
Debt will tear us apart (@rruthology)

Every single codebase has its own technical debt, what defines technical debt, in simplicity, is how much the effort and time you would need to spend to fix legacy codes. We can’t escape from it but we sure can minimise its impact.

I come from a software consulting firm, our go-to framework for building web apps is Laravel, we have taken on some pretty gruesome tasks of maintaining legacy software, especially Laravel, imagine its 6.0 today and you’re stuck with maintaining a 4.0 project without any technical documentations or handover by past developers, this provided us a painful yet fulfilling insights into how technical debts are created and how they can be avoided.

Here are some of my personal recommendations for avoiding technical debt in Laravel (some can be applied to other non-Laravel projects too!)

1. Avoid hardcoding at all costs

Great example of what we encountered is hardcoded values for certain table columns, for instance, a Post model has a status(tinyInt) column, the status has a fixed set of values which are :

1 for published, 2 for draft, 3 for rejected.

You would normally do: Post::where('status', 1)->get();

What you should do, in your Post model define constants for Status,

class Post extends Model {    const STATUS_PUBLISHED = 1;
const STATUS_DRAFT = 2;
const STATUS_REJECTED = 3;
}

Then you could just query by Post::where('status', Post::STATUS_PUBLISHED)->get();

In the future where there are changes to the status values will save you the trouble of searching for every occurrences of the hardcoded values. If you have a lot of statuses to maintain and it keeps on changing, you can improve this through maintaining the statuses on a separate table then relate them to your Post model.

2. Mastering Eloquent, avoid DB::table() and DB::raw()

Eloquent is a pretty powerful ORM, you can easily switch between Database engines without even rewriting your queries if you make full use of Eloquent queries. In our work, the hardest part of saving a project from technical debt hell is to refactor ancient queries that refers to deprecated tables or columns, these queries are using DB::table() and DB::raw() to generate raw SQLs.

Don’t

DB::table('posts')->get()

Do

Post::all();

— — —

Don’t

$reads = DB::table('posts')->select(DB::raw('SUM(posts.reads) as totalReads')->first()->totalReads;

Do

$reads = Post::sum('reads');

— — —

With such usages, table names and how to access its data will no longer be a problem for refactoring as they are abstracted by Eloquent models.

You can check out all the Eloquent way of writing your queries and the magical Collection methods. (seriously, collections are amazing)

3. Don’t double the work, use Traits and Helpers

We are blessed with a ton of helpful functions provided by the Laravel framework, yet there are times that we require to implement a certain logic multiple times across our codebase. If we were to retype the same piece of code, it would lead to code redundancy and ultimately the dreaded debt builds up, maintenance would start to bite.

Traits are meant for code reuse, it injects methods directly into your classes, you can find Traits usage throughout Laravel framework, check our your User model, see use Notifiable; ? This injects notification methods to allow a Model to use Laravel Notifications, we can place this trait in other models too just by adding use Notifiable , instead of repeating notification methods in each of our model, we just include this trait and its done, handy isn’t it?

Some good resources in making your custom traits:

Helpers on the other hand, is for defining global functions accessed throughout your application. For instance, you are always accessing auth() global method to obtain current authenticated session.

We can define our own Helper functions by creating a Helper.php file in our app/ directory, then in composer.json include the file under autoload:

"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/"
},
"files": [
"app/Helper.php"
]
},

With Traits and Helpers, we could reuse pieces of our code everywhere, and centralising them into a single implementation which you would be thanking yourself during maintenance.

4. Smaller footprint classes and methods.

I’m not a big fan of object oriented programming, I tend not over-abstracting my codebase to the point of you need several cmd+B(go to definition) to get to the ground level of how a class works.

In our team, we practice a more functional style of writing our classes and methods, we try to avoid over extending or coupling our classes/methods tightly. Classes should have clear methods to carry out its purpose, example, we are a big fan of Laravel Resource controllers, where there are only 5 methods: index(), show(), create(), edit(), update(), destroy()

These methods are clearly for CRUD functions of a resource(eg. Post), all its logics are handled within the methods itself and no where else, this improves code visibility and helps reducing technical debt.

Methods, should contain less parameters, anything beyond 4 parameters is a big no for me.

public function getSomeData($a, $b, $c, $d);

public function getSomeData($a, $b, $c=null);

This again really helps with code visibility and commenting it should be a breeze. From my experience, less parameters will result in a much shorter and clearer method logic implementation. Reducing long complicated methods ultimately bring down your project’s technical debt and improves maintainability.

5. Don’t be afraid to refactor.

When we are presented with a choice to refactor or start anew, most teams would opt for building a new one ground-up since they might feel its overwhelming to take on a project with in-measurable technical debt and uncertainties.

Before considering a full rebuild, we should look at the timeline and business constraints of a complete rebuild, typically, businesses would not want to spend the costs of rebuilding a working product and is expecting to maintain it for a long time. With this, refactoring should be a much better choice, yes we may be faced with horrors of the debt but like my team, is a valuable tool for you to understand how the debts are created and resolved, with the experience, you and your team definitely become better developers.

(P/S: Of course, if its a one-off, smaller scoped project, rebuilding is a faster solution)

It all comes back to just one thing, better be ready than sorry, the effort you spend ensuring your code quality is up to standards and is in check cross all of your team members (incl. new comers too!) will give your team an edge over other Laravel projects ridden with technical debts.

--

--

Elson Tan

Full Stack Web Dev // Certified AWS SA // I run a Web Dev Agency (nedex.io). Twitter @elsodev.