Contributing to Open Source 2: Be a Mimic

Published 13/01/2022 | 1814 views

It can be tempting to dive right in with your PR idea. But taking the time to get to know the codebase and author can pay off immensely. Here's why.

Don't rush in to a PR. Take time to learn from the author.

If you haven't already, click here to check out my first post in this series, all about finding the right project.

So, you've found a project. Congratulations! That's half the battle. You're raring to go, but where to start? Perhaps you feel you should just start hacking on your problem, tearing the codebase to pieces and shoving your binary self somewhere in the middle.

Stop.

Open source is so much more than getting something to work. It's an opportunity to learn, to teach and to engage. Let's walk through how I tackle contributing to a new Open Source project. I'm not the oracle of Open Source by any means, but I might have a few useful tidbits for you.

Get to know the code

Once you've forked the codebase and have it running in your IDE, before branching and hacking on your contribution, take 15 minutes to look around. How is the application or package structured? If it's a Laravel site or Laravel package, here's my ordered checklist:

  1. Search for ServiceProviders. These tend to be where all configuration happens. You'll get indication of which interfaces you should take note of. You'll see how the auth layer is configured. You'll get an idea of routing.
  2. Check the route files. This will likely be routes/web.php. This gives a nice overview of the controllers available to you and what they do.
  3. Go through several controllers. Does the author prefer to do the main body of the work in there, or do they offload to an action or service class? How is validation handled? Do they do it inline, or use a form request? Do they prefer using dependency injection or Facades?
  4. Now to the tests directory. Is there a preference for unit tests or feature tests? Which test framework are they using? How are the tests organised?

Make a mental or physical note of all of these findings. We're going to use them in a hot second.

Make sure the tests run

This step is vital. You want assurance that your code doesn't break anything else in the project. So, before you start, run the test suite once. Many good repos will have a composer script setup for this, so you can run something like

composer test

to quickly perform all needed checks.

Whilst you're looking for that script, take note of other scripts in the composer.json file. For example, here's what Pest's scripts config looks like.

"scripts": {
"lint": "PHP_CS_FIXER_IGNORE_ENV=true php-cs-fixer fix -v",
"test:lint": "PHP_CS_FIXER_IGNORE_ENV=true php-cs-fixer fix -v --dry-run",
"test:types": "phpstan analyse --ansi --memory-limit=-1 --debug",
"test:unit": "php bin/pest --colors=always --exclude-group=integration"
}

This gives me indication that the author (Nuno Maduro) expects my code to be linted, to be type-hinted using PHPStan's ruleset, and to have a fully working suite of unit tests. All good things to keep in mind.

Once you have the tests working, it's time to start coding.

Be a Mimic

This is the key part of this post. With all the knowledge you gained from step one, it's time to become the author of the project. You need to code like them. Why, you ask? For two main reasons.

Your code is more likely to be merged

Think about it. If the author of a project comes to review code, and it's full of concepts, principles and ideas they're unfamiliar with, or worse, dislike, you aren't going to get very far.

At the end of the day, you aren't the one who will have to maintain this code for the foreseeable future, they are. So, give them the respect they deserve. By imitating their style, even if it's a little contrary to our preferred way of writing code, we make their lives easier, make our code more familiar, and increase the chances of them saying yes to your contribution.

Here's an example. I recently contributed a significantly sized PR to Laravel.io. I'll be going into why I contributed this featured in a future post. One thing I noted was the way Dries had opted to use jobs as a form of action class. Here's the job for banning a user.

<?php
 
namespace App\Jobs;
 
use App\Models\User;
use Carbon\Carbon;
 
final class BanUser
{
public function __construct(
private User $user
) {
}
 
public function handle(): User
{
$this->user->banned_at = Carbon::now();
$this->user->save();
 
return $this->user;
}
}

When it comes time to actually ban a user, Dries dispatches the job synchronously, like so.

public function ban(User $user)
{
$this->authorize(UserPolicy::BAN, $user);
 
$this->dispatchNow(new BanUser($user));
 
$this->success('admin.users.banned', $user->name());
 
return redirect()->route('profile', $user->username());
}

I'll be honest, I don't love this approach. I much prefer a dedicated action class that can be injected into a job, or a controller, or an artisan command. But when it came time for me to add support for generating API tokens for users, I followed suit.

<?php
 
namespace App\Jobs;
 
use App\Models\User;
use Laravel\Sanctum\NewAccessToken;
 
final class CreateApiToken
{
public function __construct(private User $user, private string $name)
{
}
 
public function handle(): NewAccessToken
{
return $this->user->createToken($this->name);
}
}

When Dries reviewed my PR, he wasn't confused because I'd introduced another paradigmn. He understood the code immediately. In fact, I imagine it wasn't far off what he would have decided to implement himself had he the time to do it.

Because of that decision, my PR was merged. There were no major changes requested. All went smoothly, and everybody was happy. If down the line Dries or Joe need to make changes to the code I contributed, they'll feel right at home and will be able to do so quickly and efficiently.

You'll learn a lot on the way

Rather than sticking to your guns, by widening out and learning from other's approaches to programming problems, you'll learn a lot yourself too. For example, I never realised that you could retrieve the return value from the handle method of a Job!

There are so many patterns, paradigms, hidden framework features and more than I've picked up by studying other's code in this manner. Obviously, when you pick up something in this way, you can then carry it over to your own projects.

What else have I learned from applying this methods over the years?

  • The principle of dependency inversion by contributing to Laravel
  • PHP CS fixing, in-depth PHPStan and evaluated PHP code by contributing to Pest
  • Complex HTTP requests and configuration by contributing to Spatie Ray

Many of us would no doubt snatch up the opportunity to have a personal tutor sit with us and show us the best way to accomplish various technical tasks, even if it cost a fair amount of money. With Open Source and this methodology, you get 1000's of personal tutors for free!

Wrapping up

So, before you get started contributing, whether it's your first time or you're a seasoned veteran, take a break. Browse the codebase. Get into the mind of the author. Then, with your own flair, pick up their paintbrush and mimic their style.

If you really think you have a better way of doing things, suggest it to them. More often than not, though, you'll come away with a merged PR and new skills added to your dev toolkit.

Kind Regards, Luke

Like what you see?

If you enjoy reading my content, please consider sponsoring me. I don't spend it on cups of coffee; it all goes towards freeing up more of my time to work on open source, tutorials and more posts like this one.