Git Repository

Part 1, Chapter 3

Initial Setup

First things first -- let's create a new project:

$ mkdir tasks
$ cd tasks
$ git init

Add a new .gitignore file. Then, add the contents from the Python .gitignore template found here.

Add Serverless-related directories to .gitignore as well:


Add the .gitignore to git and commit:

$ git add .gitignore
$ git commit -m 'Initial commit'

Although you could add the .gitignore to git later, it's a great idea to do that right away to avoid adding unwanted files to git.

Create a new account on GitHub (if you don't have one already). Next, log in to GitHub and add an SSH key to your account (again, if you don't have one already). Go back to the home page and click on "New". Set the project name to tasks.

After your project is created, copy it's SSH URL.

Next, add the new remote and push your initial commit:

$ git remote add origin [email protected]:<your-github-username>/tasks.git
$ git push -u origin main

Don't forget to replace <your-github-username> with your actual username

Mono-repo vs Poly-repo

The decision here is simple: Always start with a monorepo. It doesn't matter whether you're building a Django full-stack monolith, a JavaScript SPA with a backend RESTful API, or a serverless microservice -- each new repository is a new thing to create and, more importantly, maintain. It's much easier to track issues, PRs/MRs, commits, and deploys when things are kept in a single place. That doesn't mean you should never split your single repository up into multiple repositories.

If some part outgrows the monorepo you can move it into a new repository. It makes sense when the development for this part becomes completely independent. For example, an internal Python library that's used across multiple projects. It should have its own development cycles, releases, issue tracking, and so forth. Once it's moved, other projects can then install it directly from PyPI.

Repository Structure

Building high quality, full-stack software requires much more than just putting together a web app. We have to build libraries, manage infrastructure, develop APIs and UIs, and write scripts, etc. Therefore, we need a repository structure that will allow us to match that complexity without making a mess. We'll use the following folder structure:

  • .github
  • infrastructure
  • services


The easiest way to manage servers, subnets, load balancers, managed databases, etc. is by using infrastructure-as-code (IaC). Inside the "infrastructure" folder we'll store configuration files for our tool(s) of choice. These are usually long YAML, JSON, or HCL files.


Anything that is long-running or talks to our end users is considered a service. It can be monolithic Django application, Vue UI, or a couple of microservice APIs -- all will reside in this folder.


We need to configure our CI/CD pipeline. That's usually done via a YAML files, which will be stored in the ".github/workflows" folder.


When and how often should you commit? What should you write as a commit message?

Another topic that usually increases the heat in a room full of developers. While there are many guides on this, let's keep it simple. For commit messages, why do we add messages to a commit in the first place? After all, each commit has its unique hash. The answer is quite simple: To know what changes we did in the commit. Simple as that. So do just that in your messages:

Increased max body size from default 2.5 MB to expected 10 MB


Describe what you did and add reference to the issue so you can keep track of it. The reference in this example is from an issue on Jira but you can do something similar for GitHub or any other issue tracker.

Next, when and how often should you commit? Commit as early as possible. If you're following Test-driven Development (TDD), commit after each test passes. Make sure your commits are small; it will be much easier to track changes and perform code reviews.

What Have You Done?

In this chapter, you created a new git repository for your project and added a .gitignore template designed for Python projects. At this point, your project structure should look like this:

└── .gitignore

Mark as Completed