In my first startup Tabulo we screwed ourselves over without a good version control system in place. A year and a half later into the project we realized we killed our developer experience with recent infrastructure changes. Thus we tried to revert to a previous version of the application before our migrations a few months prior.
Thanks to our bad system we ended up reverting to a faulty version where my teammate was in the middle of developing some feature. Yay.
Don't just have two branches (main and dev) and randomly work from there. Please. Project Management is nice.
After about a thousand commits here's how my new startup has evolved our version control and project management with Linear.
For starters we have three standard branches: dev, staging, and main. We start off by pushing code onto dev where we test everything in localhost, test Stripe keys, and local PostgreSQL databases spun up and destroyed on the fly with Docker. This is where we develop.
Then when we want to push our current site onto main we will first merge dev into staging. Staging is an environment that's extremely similar to production on another URL (for example dialect.io instead of dialect.so) that mimics the production environment. That way we can test our application on a production-like environment without screwing up production.
Here we can handle necessary database migrations and miscellaneous issues that arise when trying to push a site to production.
After we've battle tested staging we will finally merge staging into main and hope everything turns out well. Worse case we may have our site down for up to 30 minutes but we've already gone through the motions on staging so it'll just be minor unexpected behaviors.
Throughout these branches we have different sets of environment variables controlling things like site URL for redirects or which database to connect to. We control these environment variables with Dotenv Vault which I've written about previously.
The bulk of this post will be on how we manage our dev branch with multiple collaborators and features undergoing development at the same time. We use the Linear project management software to manage our tasks and goals on the development of Dialect.
This is our flow.
Let's say you're working on a brand new feature called "Demo Feature" we would create a new branch off dev titled feature/demo-feature
.
git checkout -b feature/demo-feature
Let's go ahead and push and initialize this branch on GitHub with an empty commit
git commit --allow-empty -m "Initializing new feature branch"
git push origin feature/demo-feature
Then from here we will create a project in Linear:
I'd frequently add myself as the Lead if I'm going to be the predominant developer on this new feature. In addition I'd frequently add a description to what I envision this new feature adding onto the platform.
With the Linear project created I can go inside and create new issues within the project. I typically see issues as bullet points of actions and "milestones" that will be accomplished before the new feature is ready to be merged into the rest of the project in the dev branch.
Feel free to add other issues with other users to solve them if they pertain to realms of development (such as delegating modals to Ryan).
Create the New Issue by clicking the New Issue button. Simply insert in a short title (that will later serve as a branch name) and a description. If the task is simple just a short description (or no description) may be enough. If it's complex it may be wise to add pictures and text.
If you are solving a bug a Loom video link showing the bug and steps to reproduce the bug are appreciated. Bonus if ideas on why the bug is occurring are given.
Then from the left to right we adjust the fields in our Linear Issue. We'll first set the status as "Todo" unless we want to throw it into the "Backlog" to be completed at a future unspecified time.
We can usually not set the priority and keep it blank. However if it's important then we may specify it as "Urgent" or "High".
Next we should assign ourselves (or whoever you want) to be in charge of finishing that issue.
Next we label the feature. It'll typically have a "Feature" tag but may be classified as "Improvement" if we are simply improving a quality of life in this branch.
Next up we specify which project it'll be under, but if you created it in the project then it defaults to the project you created it out of.
Lastly we have the cycle. I find cycles similar to sprints, but it's basically a quick way to specify during which period of n weeks you want to complete this task. Typically I'd set my tasks to be the current cycle (our cycles are two weeks long). Any tasks that aren't finished in the current cycle will be automatically added to the next cycle.
Here's an example page of filled tasks to complete a simple todo-list feature:
Now let's get started on tackling one of these tasks. Let's say we are working on the first task "Todo states". Simply click on it and click the top right button "Copy Git Branch".
From here we will go back to our terminal and create a new branch off the feature branch "feature/demo-feature" calling it what we copied over. In this case our command looked like
git checkout -b kevin/dia-61-todo-states
Notice that the new task branch name will always be named as [who the task is assigned to]/[team name initials (dia for dialect)]-[id]-[kebab casing of the task name] or [kevin]/[dia]-[61]-[todo-states].
Now in this branch go ahead and finish the code changes required to complete this task. You can push multiple git commits in the task or simply have a single git commit depending on how complex the specified task is.
After finishing the task simply push this new branch out and complete a Pull Request to merge the "kevin/dia-61-todo-states" branch into your feature branch (like feature/demo-feature).
Here is what pushing the new task looks like on my end in the terminal:
Make a new Pull Request:
After creating the pull request Linear will automatically notice that a pull request has been created and set the status of the task you were working on to "In Progress" if it's pending review for a PR merge:
In addition it links to the PR with the green #65. Now you can go begin working on your next task.
After someone accepts the PR and it gets merged into the feature branch Linear will automatically update the status of the task to "Done"
Later on you can clean up by deleting this branch on GitHub and then also deleting it on your local git CLI with
git branch -d <branch name>
This will auto prompt a warning if you've forgotten to merge this branch into one of your branches. To override this warning replace the -d
flag with a -D
.
And boom that's it! We'll then later merge the feature branch into dev, test it more, and later merge dev into staging and then main when we want to push a new version of our application to production.
I hope you were able to draw inspiration from our system of Version Control and Project Management into your own projects. That or just keep pushing every change to the master branch :P
Happy Hacking! 🥳💻