DEV Community

Cover image for BoG's Ultimate Dev Guide
Bits of Good
Bits of Good

Posted on

BoG's Ultimate Dev Guide

This post was written by our Exec Director of Engineering: @bholmesdev

At Bits of Good, we have a lot of projects to manage at a given time. So, we compiled a collection of setup guides and best practices our project teams can reference while building applications for their nonprofits. We believe the contents of this article are helpful to anyone getting started on a team project of their own, so read on!

Table of Contents

  1. Getting set up with Git
  2. The lowdown on linters
  3. Creating and Organizing Issues
  4. Branch flow basics
  5. Branching best practices
  6. Committing best practices
  7. Pull request best practices
  8. The lowdown on CIs

Getting set up with Git

This section is for those setting up a new project from scratch. If everything is already set up, you can skip this section. Just make sure everyone has Git set up on their machines armed with the fundamentals of branches and pushing.

First thing's first: time to set up your project with Git. If you do not already have Git set up on your machine, head over here to download the CLI tool and here to set up a new GitHub account.

All good? Cool! Assuming you've already created your project locally using create-react-app or whatever other tool, just go to the root directory of that project in your terminal and add Git:

cd PATH_TO_YOUR_SNAZZY_PROJECT
git init
Enter fullscreen mode Exit fullscreen mode

This will initialize everything for you. Now, you need to set up a repository on GitHub where you'll be able to put the project code for everyone on your team to see. The easiest way to do this is to go to the GitHub site and create the repository there. But wait... we don't want to slap this on our own GitHub profile. We want to make it under the Bits of Good name!

Head to the Bits of Good GitHub page and hit the "New" button above the list of repositories. From here, you can create your project under the BoG org. This should be completed by the team lead, and they may need to ask the Director of Engineering to add their account under the BoG umbrella.

Once this is done, set up your local project with this new repo. Before pushing everything up, make sure you have a .gitignore to ignore files that should stay local. Here is a solid list to get you started, which will likely change and evolve moving forward. Note that this list is not exhaustive! It's a good idea to carefully look through the log of all the files being pushed to the repo on your initial commit to make sure the ignore catches everything.

.vscode/  # editor-specific files to set up your workspace, assuming the team uses VS Code
.idea/  # same as above for those who code in the IDEA IDE
.DS_Store  # Mac-specific file relating to Finder. A nuisance that I forget about 9 times out of 10
node_modules/  # the big bad folder with all your packages
.env  # environment variables relating to APIs that should only exist locally

public/*.js  # all build files relating to JavaScript. May be omitted depending on the project
*.log  # any debug and error logs that may get generated
npm-debug.log*  # same as above to cover funky extensions used by npm and yarn
yarn-debug.log*
yarn-error.log*
Enter fullscreen mode Exit fullscreen mode

Alright, time to push! Thankfully, GitHub already explains how to do this on the landing page of the repository you created. Just paste everything into your terminal under the header "…or push an existing repository from the command line."

Adding people to the team

Now that you have a shiny new repository set up, it is time to invite everyone on the team so they can start pushing wonderfully (totally not broken) code as well. This is pretty simple: go to "settings" on your new repository page and choose "Collaborators and teams."

GitHub team settings

You can just add everyone individually, but it is much easier to create a new "team" with all members included. To do so, choose "Create a new team," add a name for it (preferably the name of your nonprofit), and add that team to the project. After this, you'll be able to add write permissions to the team, and clicking the team itself will allow you to add new members. I know, that last part wasn't intuitive to me either. Make sure your designer is working hard to avoid confusion like this πŸ˜†

The lowdown on linters

In an ideal world, all the code you ever type out would look absolutely perfect and be a joy to read. But have you ever had to run "checkstyle" on your Java homework only to find 10 billion errors built up while pulling your all-nighter? Yeah... it's not easy keeping code neat and tidy.

Because all developers are lazy by nature and want to automate everything, there are now some amazing tools at your disposal to make EVERYTHING pretty at the click of a button: ESLint and Prettier!

The purpose of these two are fairly simple: ESLint will give you some lovely red squiggles under code that could cause errors or unwanted behavior, and Prettier works to auto-format the code's styling. All you have to do is configure some styling rules and ESLint + Prettier will take care of the rest.

Setup guide

Let's get this magic set up in everyone's editor. You can use the default configurations for everything and your code will look nice enough. However, the company Airbnb has created their own rules for errors and styling that has become super popular across the web dev landscape. So, let's set up their rules in our own project.

To install all of their packages for this, type this one liner in your terminal:

npx install-peerdeps --dev eslint-config-airbnb 
Enter fullscreen mode Exit fullscreen mode

Let's break this down. npx is a fancy addition to npm that allows you to install and execute a package at the same time and dump that package when you are done. In this case, we're executing the "install-peerdeps" package, which installs all the peer dependencies for Airbnb's ESLint configuration. We also add the "--dev" flag to install all the peer dependencies for developers only, since we shouldn't have to care about reformatting code once the website is actually deployed.

Now for the autoformatting we discussed earlier. This Airbnb package just handles the ESLint side of things. To add Prettier to the mix for styling fixes, install the following packages:

npm i --dev prettier eslint-config-prettier eslint-plugin-prettier
Enter fullscreen mode Exit fullscreen mode

The first package is just Prettier itself, while the latter two make it play nicely with our ESLint configuration. With all the packages installed, it's time to configure them to talk to each other. Create a file in the base directory of your project called .eslintrc and paste the following:

{
  "extends": ["airbnb", "prettier"],
  "plugins": ["react", "prettier"],
  "rules": {
    "react/jsx-filename-extension": [
      1,
      {
        "extensions": [".js", "jsx"] // allows you to write React code in ".js" and ".jsx" files
      }
    ],
    "prettier/prettier": "error",
    "max-len": ["error", 120] // lines can be 120 characters long before they should break
  },
  "env": {
    "browser": true
  }
}

Enter fullscreen mode Exit fullscreen mode

The first two lines are doing the bulk of the work here, telling your project that you are using Airbnb's ESLint config along with Prettier. The "rules" section features a couple of overrides to make your life a little easier, allowing you to write React code inside .js files along with .jsx files, and adding a check for line length to decide when the break. I prefer 120 characters as the limit, but your team can change this to whatever they prefer.

Just two more minor additions. First, add a .prettierrc file to the base directory containing the following:

{
  "singleQuote": true,
  "trailingComma": "all"
}

Enter fullscreen mode Exit fullscreen mode

These are two styling overrides that can be omitted (or augmented) depending on team preference. The first requires all quotes to be single quotes by default, and the second will add a comma to the end of every last key in an object. This is super useful when you go back to add another key to an object, since you won't have to remember to add that silly comma to the previous key.

Finally, let's add a script you can run to do an ESLint check across the entire project. For this, add the following to the scripts field in your package.json:

"lint": "npx eslint src -c .eslintrc --ext js,jsx"
Enter fullscreen mode Exit fullscreen mode

This allows you to run npm run lint in the terminal to do a quick scan of all the linter errors and warnings that should be fixed. It's up to the team to decide which warnings / errors are worth fixing and which can be ignored.

Bonus: If the team is using VS Code as their editor (which they should!), install the following extensions: ESLint and Prettier - Code Formatter. These will give you those red squiggles and auto-fixes that are super helpful for formatting. also be sure to add this line to your editor's settings.json to format on save: "editor.formatOnSave": true

But what about Babel? πŸ€”

If you haven't heard about it, Babel is an extremely valuable tool to make all the shiny new JavaScript you write work in older browsers. It does so by compiling new JS syntax down to older syntax (say, down to the ES5 standard). This is an important addition for any project that's even remotely likely to be used in Internet Explorer (gross, I know), so I would highly recommend getting this set up. However, the method of configuring Babel seemingly changes with the seasons and certainly changes depending on the framework used, so this is best left to you for the tough Googling. Also feel free to reach out to the Director or Engineering or other experienced club members to work out the kinks.

Resources

A lot of the configuration described was inspired by this post on setting up ESLint and Prettier with Airbnb's config.

Creating and Organizing Issues

Before jumping into modifying the codebase, let's talk about organizing what needs to be done first. A nifty GitHub feature is the option to create "issues" to break down tasks into assignable chunks. This is helpful in a team setting to decide on deadlines and priorities to keep everyone on track.

What a good issue should say

Let's start with an example of a bad issue:

Example of bad GitHub issue

Not only is it unclear how the final result should look, but it's nowhere near enough direction to keep a newbie's head from exploding. Do everyone a favor and break issues down!

GitHub themselves are nice enough to include example headers whenever you create a new issue. These are super nice for getting started, but may prove to be a little too much for the issues you create. At a minimum, try to include:

  1. If it's a feature: A description of the feature being added to the project
  2. If it's a bug: A description of what the bug is and how to replicate it
  3. A breakdown of action items. This should be a list (with checkboxes preferably) of bite-sized tasks to complete in order to finish the feature / resolve the bug
  4. General notes for the feature / bug or improvements that may be needed down the road
  5. Discussion points if the team still needs to decide on certain details

The action items should be the bulk of the issue. You can get as granular or technical as you want, so long as it makes someone's life easier trying to read the issue. Good ideas are to include hyperlinks to potential solutions and supporting images like mockups as a guide.

Optional: Break down the action items like a tutorial, walking the reader through one potential method of coding out the issue. This is great for new team members so they don't feel lost navigating an unfamiliar codebase.

Here's an example of how to beef up that confusing one-liner issue from earlier:

Example of good GitHub issue

Leveraging milestones

If your team happens to use Agile development or another form of rapid release cycles, you should probably avoid chucking issues around without any reference for completion timeframes. GitHub allows you to get a focused view of what you're working on for each dev cycle / sprint using "milestones." This is little more than a subset of issues in the repo at a given time.

To view these, look for the "milestones" button within the "Issues" tab. From here, you can create a new milestone with a useful title, description, and deadline. You may tack an issue onto a given milestone using the sidebar when creating a new issue. In general, make sure you don't have too many issues in a given milestone. It may take some time to feel out how long certain tasks will take, so stay optimistic in the beginning and pull back depending on the workload.

Example of a GitHub milestones setup

A look at our team's past milestones working on an Agile development schedule. We found emojis can help focus milestone objectives πŸ˜›

Branch flow using Gitflow

It's easy to jump into a new project and immediately think "screw the formalities, I'm gonna jump into some code!" Well, it's easy to forget an important detail when blinded by excitement: you should always branch off of a development branch first!

This approach is often lost on beginner Git projects, branching off of master alone and merging in whenever code gets the thumbs up. This approach works well on small-scale projects that are only seen by developers most of the time. However, When users get involved that may access the live project at any time, pushing to a "production" branch like master for every change may not always be a good idea. For example, say the you're on a team building a notification dashboard, with two developers splitting up the UI and a third working on API endpoints. Instead of pushing to master or working off of some awkward middle-way branch, wouldn't it be nice to push to a develop branch to merge everyone's changes gracefully?

This process is known as Gitflow, using a develop branch for building out features and a master branch for the user-facing code. Other flows include further branching stages such as a staging branch, used for testing and validating code before it is merged into the master branch.

Setting up this flow is extremely easy in Git. Just leave the default master branch, treating it as the production branch, and checkout a new develop branch off of master to branch from going forward. Also make sure all pull requests made to the project are targeted at this branch instead of master! The only PR to master should be from the develop branch itself most of the time.

Branching best practices

If you've been through a workshop or basic Git tutorial before, you've probably made branch names like Ben-Holmes, bug-fix, or homepage-57. Please don't do this!

As a general rule, try to title branches like an overarching todo task for yourself. So, it should be descriptive enough that you know exactly what you're working on, but kept to 5-7 words or less so it's not impossible for teammates to type. In addition, the scope of a branch should be more focused than broad so all your commits are working on a common issue. This has an added bonus of avoiding little "side quests" while you're coding. If you see a little spelling mistake on the splash page that has nothing to do with what you're working on, don't make the fix in your current branch!

Formatting

You can use whatever works best for the team of course, but a solid practice is to:

  1. Start the branch name with the author of the branch. This way, if you run git branch -a in your terminal to see all open branches, you can quickly pick out ones you're working on.
  2. Include the issue number so you can reference it in your pull request.
  3. Add a title for the issue being resolved. Again, make it short and descriptive!

Let's see an example

Say you’re working on issue #17 titled "Add profile picture editing to the 'My Account' page." As the title implies, you are adding an option to the user's account management page to edit their profile picture, say, when they click on the image. Here's a decent branch name for the issue:

git checkout -b CarlyRae/17/my_account_edit_profile_pic
Enter fullscreen mode Exit fullscreen mode

Yes, this is a pretty rigid structure for a simple branch name. However, it really improves scan-ability when there are tens or even hundreds of branches open at a given time!

Committing best practices

It's the end of the day. You've exhausted your third cup of coffee and finally squashed an Internet Explorer bug that took hours to track down. Now you're ready to push up your changes and finally shut your computer. So, what's the best way to communicate all the pain and suffering you worked through with the team?

git commit -m "fixes"
Enter fullscreen mode Exit fullscreen mode

πŸ˜“β˜•οΈ Well... it's definitely not this. The commit message isn't some throw-away blob of text that no one will ever look at. This is the developer's chance to explain what their code is meant to accomplish, so when a peer combs through the log of changes you made, they will be able to step through the commits and track which changes are worth adding. This is invaluable for the occasional rollback, when changes made beyond a certain commit should get scrapped.

Writing a good commit message

So, how can you make the most of a commit message? First, it's best to start with some verb to describe what adding the commit to the project will accomplish. This is often a present tense phrase like "add," "fix," or "include."

Following this, include a brief phrase to describe the commit's purpose, usually in 50 characters or less. If you use VS Code, the built-in commit menu (the third button down in the sidebar) will do this check for you. This limit should be a check for yourself on how focused the content of the commit actually is. For example, if you were committing a bug fix for an out-of-place profile picture in Internet Explorer, a solid message might be:

git commit -m "fix profile picture position on homepage IE"
Enter fullscreen mode Exit fullscreen mode

Spacing out your commits

Yes, that was a pretty easy example to contrive. Commits can easily span many more files and many more features. Always try to fit within the character limit as well as you can, but if there's too much to explain, it might be worth breaking up the commits into smaller chunks.

This can sounding daunting at first, but Git offers a handy tool for this when you're staging everything for a commit: git add -p. Rather than staging all your changes at once, this allows you to walk through each change to the codebase you've made file by file, giving you options to stage at each step. Here's a sample output staging some ESLint configuration files:

Example output of git add -p

Here, we see a script added to the file package.json denoted in green with a "+" icon. We also see a number of ways to stage that edit denoted in blue. If you're unsure about what all those random letters mean, just type the "?".

This can be a tedious process for larger commits, but is super helpful when just starting out with Git. It's also safest to commit like a madman so your changes are easy to summarize, and you won't have to do this walk(through) of shame as often.

Extended reading: Much of this section comes from experience and various Stack Overflow sources, but this is a great long-form read on quality commit messages.

Pull request best practices

Everything about Git we've talked about so far has been pretty standard fare: you read through an issue, you make a branch, you commit code to it, you push it up. It's easy at this point to push everything to the development branch and call it a day... but hang on a minute! If you're working on a team, it's probably best to have someone else review the changes you've made before merging them in.

The first best practice for pull requests are to... just do them 🀷 Whenever an issue is resolved by the branch you've created and your changes are pushed up, whip up a PR.

There admittedly isn't a great way to do this from the terminal, so it's likely easiest from GitHub directly. Note that the purpose of a PR is to allow others to review your changes before merging into another branch (most always the development branch), so be sure you have already rebased the remote master / develop branch onto yours to make reviewing changes a bit easier.

Note: If you are unfamiliar with rebasing, it is essentially merging but with an adjustment to the commit history. With rebasing, all commits in the remote will be placed before your own commits, making it seem like your branch is building off of the new remote. This article has a nice visual aid for showing how that works.

Once the pull request is made, add people for review so they will get notified. It's usually best to reach for people who did not work on the change with you so they can review your approach with a fresh set of eyes. However, if you are a fan of pair programming, it's possible to bypass the review process after having a solid back-and-forth with your partner.

Example of a good pull request

Here's an example of what I'd consider a good pull request. First, note that the team's lead was requested for review (shown by the checkmark in the "reviewers" section). Also note that the comment box offers a detailed breakdown of what changes were made in the branch. This can be structured however you choose, but it's best to at least:

  1. Include the issue number so the reader can reference what is being resolved
  2. Write a bulleted / numbered breakdown of each feature added, or changes made to resolve a bug

Below this, there is a log of each commit made in the branch requested for merging. This shows how important quality commit messages are!

Beyond human checking, it's also good to install automated tools to run some diagnostics on the modified code to make sure everything still builds and runs. This brings us to the next important section...

The lowdown on CIs

Now that the team (hopefully) has a solid workflow on Git, it's time to throw some automation at the review process. Entire textbooks could be (and have been) written on automated testing, but for the sake of this guide, we're just going to go surface-level with it 😊

Here's a general overview: CI stands for "continuous integration," where "integration" refers to testing how the code works as if it were deployed onto the web, and "continuous" refers to making these checks time and time again with each update to the codebase.

The tool we're going to touch on here, CircleCI, wraps up this concept into a single resource where you may deploy your app into its own testing sandbox. It does so using a virtual machine CircleCI is kind enough to host themselves where the project can be installed, built, and run. CircleCI brings a host of other benefits to the table, namely offering GitHub integration and sending out notifications to the team for any build failures. The former is what enables our pull request checking we will get to in a bit, but first, time for a quick example!

Testing a React application

Here, we're going to run a CircleCI test on your React frontend application.

Before coding anything, have the team lead head to the CircleCI sign up page to authenticate using their GitHub login. Defer to the DoE to make sure this account gets added to the Bits of Good team so all projects can get tracked under a common name.

Next, let's set up a simple config file. CircleCI handles all the heavy lifting setting up the VM without you needing to SSH into it, so all that's necessary is a series of steps CircleCI needs to run through to test the application.

In the base directory of the project, add a .circleci folder with a file inside named config.yml. Yes, this will be a file written in YAML, which you can think of as a simplified version of JSON. Inside, you will need the following:

  • The version of CircleCI to use
  • What docker image to use (in other words, make sure the VM has NodeJS installed)
  • Add a run step to checkout the code and install all the dependencies using npm install
  • Add all the tests that should be run on the app if it builds successfully. There is a simple command called npm test which works out of the box for create-react-app applications to run test files.

Here's how the config file might take shape:

# .circleci/config.yml

version: 2
jobs:
  build:
    docker:
      - image: circleci/node:8
    steps:
      - checkout
      - restore_cache: # special step to restore the dependency cache
          key: dependency-cache-{{ checksum "package.json" }}
      - run:
          name: Setup Dependencies
          command: cd frontend # optional step if the React code exits in a "frontend" directory
                  | npm install
      - save_cache: # special step to save the dependency cache
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - ./node_modules
      - run: # run tests
          name: Run Test and Coverage
          command: npm test
      - run: # run the linter script added to the package.json, if applicable
          name: Run Linter
          command: npm run lint

Enter fullscreen mode Exit fullscreen mode

As a precaution, try running these config steps in your terminal to make sure they are working locally. For example, you should be able to cd into the frontend directory and run npm test to execute any tests that are there. if the app was created with create-react-app, there should be a default test file called App.test.js that looks something like this:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
  const div = document.createElement('div');
  ReactDOM.render(<App />, div);
  ReactDOM.unmountComponentAtNode(div);
});
Enter fullscreen mode Exit fullscreen mode

You could probably guess by the renders without crashing label that this test makes sure the base app renders without a hitch. Note this test does not take in any example data or assume anything about user logins or API calls! These are all much more specific tests that deserve careful attention while working on your application. This is a whole can of worms that should be opened up slowly as the application matures. At the very least, make sure these tests are hit:

  • Unit tests on each API endpoint. This can be a simple call that makes sure of what datatype gets returned by the API. Digging deeper, you may explore integration testing to actually poll a "mock" database and make sure of exactly what data gets returned.
  • Successful rendering of all base components on the frontend, which may just be App.js depending on how dynamic the application is.

With those set up, CircleCI should be ready to go. Now, whenever a pull request gets made to the repo, CircleCI will run through the codebase again to make sure everything builds and renders successfully πŸ₯³

Example of CircleCI check on a pull request

Resources

This section was loosely guided by this tutorial on setting up continuous integration within a React application.

Wrapping up

After reading through this guide and getting everything set up, I hope you feel a little more confident building a web applications with a team. All these suggested rules aren't meant to scare you; they're just here to make everyone's lives a little easier when trying to organize code.

Also, Bits of Good isn't just about stuffy Git rules and coding all day. We really want to foster a sense of community, so building these Nonprofit tools is less of a chore and more of a chance to collaborate and hang out with other code-loving individuals.

To learn more about our mission, visit bitsofgood.org!

Hope you're as excited about this coming year as I am! πŸ˜ƒ

Top comments (1)

Collapse
 
ahijaouy profile image
Andre Hijaouy

Amazing guide! I think this will be super helpful for teams for years to come :)