This article is loaded with my personal opinion on how to do things. The baseline I'm going to describe is derived from my personal experience. If you have a different view on some topics, please let me know. I'm not claiming that what I'm doing is perfect!
When I'm talking about a project in this post, I'm thinking of a larger, commercial project. Not something you'd do on the weekend. When I try out stuff on my own, I also choose not to do some of these steps. However, once you're working with a team on something you want to sell to customers, I'd go with the list below.
That's the first time the TL;DR section is straightforward. Have a look at the buzzwords and decide for yourself whether you want to read more or not.
- Define the terms you use,
- Automate your tasks,
- Format your code automatically,
- Zero tolerance for lint errors and warnings,
- Check for cyclic dependencies,
- Tests!, and
- Automate deployments
This step is often forgotten or declared not crucial because it isn't so much fun. You can't play around with technology, and you have to sit down and talk with your teammates. Probably for a couple of hours, even, and I still think it is precious.
You should - as a team - come up with definitions for the words you use. Did you think "bug" means the same to everyone? I got news for you: It doesn't.
That's why it is essential to come to a shared understanding of what these terms mean for your team. The good news is that you can then publicly share the definitions so that other people know them. Especially the fine line between a bug and an improvement is crucial.
Here is a non-exhaustive list of things you might want to define.
- Ready (how must a ticket look before you start to work on it)
- Done (what needs to happen so that you consider a task done)
- User Story
- Idea (e.g., what do we expect to gain from it)
If a step in your process is essential, you should look into automating it. It shouldn't be necessary for people to know the process in and out to follow it. All of the activities I describe work best when they run once you push code to your repository.
It doesn't matter which platform you pick. Here are some that I've worked with previously.
Your company might already use Jenkins. I'd guess the likelihood increases with the age of the company you're working in. I must say I've had more bad experiences with it than good. That's not because Jenkins is terrible but because it was always self-hosted. Unless you're in the business of task automation for developers, you probably shouldn't host these things yourself. Pay a company to do it for you.
GitHub Actions are extremely powerful. You'll need to host your code on GitHub to use them.
The most irritating thing that I encountered was that you don't want to build what GitHub calls an "action" in most cases. You want to create a workflow. Creating your action will probably be a sporadic activity. More likely, you'll use actions that others have built already.
It took me way longer than I'd like to admit to figure this out. And if you don't figure it out, you're set up for a pretty complicated ride.
In contrast to CircleCI, each workflow will live in its file, which makes it easier to create workflows with concise concerns. However, you'll need to redefine tasks like
install that might be the same for each workflow (or you'll create an install action).
It would be best if you didn't argue about code formatting. Use a tool like
prettier and never speak about the topic again. I don't put any effort whatsoever into formatting my code anymore. When I save, and nothing happens, this is a sign that there is a syntax error somewhere.
Also, don't trust the "But we all use the
prettier plugin for [IDE]." Someone won't, or someone will have it configured in the wrong way. The last project where I got this answer had violations in 1500 files, and everyone was sure all code was formatted correctly.
You should have this check in your pipeline for when the tool itself is updated. They might have changed a rule. In this case, you should probably reformat the whole codebase inside the PR that introduces the update.
Most people agree that you shouldn't have any lint errors, and I'd say you also should not have any warnings. If you allow warnings, you're going to end up with pointless discussions around whether an error should be a warning or a warning should be an error, or which warnings are safe to ignore. Save yourself time and fix all warnings and errors.
I would also suggest that you agree on a set of rules that make sense in a way that there is a good reason for them to be there. I'll give you an example.
Every link that has
target="_blank"set must also define
I consider this a good rule because it statically checks for a genuine threat. If you don't set these attributes, you open up your page to one of the most prominent attacks on the internet.
Imports must be sorted alphabetically
The order of imports does not have any effect on your code. Rules like this one are the personal preferences of some individuals. If you want to enforce them, look at the section about automatic code formatting. Put this stuff in there. Don't make developers actively think about these things.
🙏 This. 🙏 Is. 🙏 Not. 🙏 Important.
I had to learn the hard way that cyclic dependencies can cause real trouble. Some of the weirdest issues arise when you introduce cyclic dependencies into your codebase. You have a cyclic dependency when two modules depend on each other. Sometimes this is easy to spot, but it becomes more challenging the larger your codebase gets. A cycle of
A > B > C > D > A is tough to track down but can cause you real trouble.
So far, I've always used a tool called
madge, which saved my ass countless times.
The biggest issue with cyclic dependencies is that they don't always immediately lead to trouble, and they are ticking time bombs in your codebase. And who likes these?
Here are some sentences I don't want to hear.
- This is just a prototype. We don't need tests.
- We need to move fast. Tests would only slow us down.
- This code is too complicated. You can't test it.
Yuck. Yuck! YUCK!
I've blogged about this here, here, and here so I'll keep this short. Testing means understanding. If you think you don't need to understand what you're doing before doing it, go ahead. I really doubt that this makes anyone any faster. The only thing you get is the illusion of speed because you don't know what you're missing.
I don't think there is any reason not to start with tests right away. It doesn't harm a prototype to work reliably. If your software doesn't break all the time, that makes you fast. If your code is that complex, please, add these tests!
To me, something is only done when users can use it. As long as a feature isn't deployed or released, it can't create value. Therefore, the step to get something out of the door and in front of users must be as easy as possible.
Way too often, it is the case that only a few people can actually deploy and then have to follow a ton of manual steps. I don't get it. We're engineers. We just made the computer do a thousand things, and then we stop at the end?
Sometimes I get the feeling that deploying is seen as something special. As if the ability to deploy is something you have to earn. I think that's total BS.
If you behave like that, it is no wonder if deploys fail often. Deploying your software should be on the same level as running your tests. While there is more to this than merely automating the process, automation is a good start.
That's my list. Is there more? Absolutely. But that's for another post.
Do you have anything to add to this list? Do you agree or disagree? Tell me on Twitter: @philgiese.