🐙
Git Developer Guide
About
  • Overview
  • Scope of this book
  • Table of Content
  • 🐢Introduction to Version Control
    • What is Version Control?
    • Overview of git and it's benefits
    • Setting up Git on Different Platforms
  • 🍼Git Fundamentals
    • Initialising a new Git repository
    • Understanding the Git Workflow
    • Committing Changes and Writing Good Commit Messages
    • Viewing and Navigating Commit History
    • Git Basics - Practice Scenarios
      • Initialising a Git Repository
      • Committing Changes
      • Exploring Commit History
      • Amending and Undoing Commits
  • 🦕Working With Git
    • What is Git Branch?
    • Creating and Switching Between Branches
    • Merging Branches and Resolving Conflicts
    • Best Practices for Branch Management
    • Git Workflows
    • Git Log
    • Git Stash
    • Working with Git - Practice Scenarios
      • Creating and Switching Between Branches
      • Merging Branches and Resolving Conflicts
      • Branching Strategies in a Team Project
      • Rolling Back to a Previous Version
      • Experimenting with Feature Branches
      • Working with Stash
  • 🤝Working with Remote Repositories
    • Cloning a Repository from Remote
    • Pushing and Pulling Changes to and from Remote Repositories
    • Collaborative Workflows - Forking, Branching, and Pull Requests
    • Resolving Conflicts in a Collaborative Environment
    • Collaborating with Git - Practice Scenarios
      • Cloning a Remote Repository
      • Pushing and Pulling Changes
      • Collaborative Workflow with Forking and Pull Requests
      • Resolving Conflicts in a Pull Request
  • 🏆Advanced Git Features
    • Aliases and Custom Configurations
    • Working with Tags and Releases
    • Rewriting Commit History with Interactive Rebase
    • Utilising Git Hooks for Automation
    • Advanced Git Features - Practice Scenarios
      • Creating Custom Git Aliases
      • Working with Tags and Releases
      • Rewriting Commit History with Interactive Rebase
      • Using Git Hooks for Automated Testing
  • 😎Git in Real-World
    • Managing a Project with Multiple Contributors
    • Integrating Git with Continuous Integration, Continuous Deployment (CI, CD)
    • Versioning Assets with Git LFS (Large File Storage)
    • Deploying a Web Application using Git
    • Git In Real World - Practice Scenarios
      • Managing a Project with Multiple Contributors
      • Integrating Git with CICD Pipelines
      • Versioning Assets with Git LFS
      • Deploying a Web Application using Git
  • Git Troubleshooting
    • Common Mistakes and Pitfalls When Using Git
    • Undoing Changes with Git - Reverting and Resetting
    • Recovering Lost Commits or Branches
    • Dealing with Repository Corruption or Other Issues
  • Git Best Practices and Tips
    • Creating efficient git workflows: writing clean code for faster reviews
    • The importance of clean code in collaborative development
    • Significance of consistent naming conventions & coding Standards
    • Good code documentation for better git workflows
    • Writing meaningful git commit messages
    • Atomic commits in git & it's benefits for software teams
    • Structuring code & managing dependencies for better git workflows
    • Git branching strategies for software teams
  • Conclusion & Next Steps
    • Recap of Key Concepts and Commands
    • Further Resources for Expanding Git Knowledge
    • Encouragement and Tips for Continued Learning and Practice
  • License Considerations
Powered by GitBook
On this page

Was this helpful?

  1. Git Best Practices and Tips

Structuring code & managing dependencies for better git workflows

Code organisation is a crucial aspect of software development that involves arranging code into logical components and modules to improve maintainability, readability, and reusability.

By organising code effectively, developers can work collaboratively, understand the codebase more easily, and make changes without causing unintended side effects.

Importance of Organising Code into Logical Components and Modules:

Codebases can quickly become large and complex, making it challenging for developers to navigate and modify code. Without proper organisation, the code may become tangled, leading to issues like code duplication, poor readability, and difficulty in identifying specific functionalities.

Organising code into logical components and modules offers the following benefits during development, collaboration/reviews and code maintenance:

  • Maintainability: When code is organised into cohesive modules, making changes or fixing bugs becomes more manageable and less error-prone. Developers can locate the relevant code quickly, reducing debugging time.

  • Readability: Well-organised code is easier to read and understand. Logical grouping and clear naming conventions make it simpler for developers to grasp the purpose of different code sections.

  • Reusability: Code that is well-structured and decoupled can be reused across the project or in other projects, saving development time and effort.

  • Collaboration: In team environments, organised code facilitates collaboration, as developers can work on specific modules without interfering with others' work.

Different Code Organisation Patterns

Several code organisation patterns help developers create maintainable and structured code. Two commonly used patterns are:

  • Modularisation: Modularisation involves breaking down the codebase into smaller, self-contained modules that handle specific tasks or functionalities. Each module encapsulates related code, and communication between modules happens through well-defined interfaces. This approach reduces interdependencies and promotes code reuse.

  • Separation of Concerns: The separation of concerns principle advocates for dividing code into distinct sections, where each section is responsible for a specific aspect of the application. For example, the user interface, data storage, and business logic should be separate concerns, allowing changes in one area without affecting the others.

Examples of Organising Code using Directories and Files

Let's consider an example of organising a simple web application that allows users to create and view posts. We can structure the codebase using directories and files as follows:

my_app/
├── src/
│   ├── components/
│   │   ├── Header.js
│   │   ├── PostList.js
│   │   └── PostForm.js
│   ├── services/
│   │   ├── api.js
│   │   └── validation.js
│   ├── utils/
│   │   ├── date.js
│   │   └── format.js
│   └── app.js
├── public/
│   ├── index.html
│   └── styles.css
└── package.json

In this example:

  • The src directory contains the application's source code.

  • Components are organized within the components directory, each representing a specific UI element.

  • Shared services, such as interacting with the API or data validation, are placed in the services directory.

  • Common utility functions are stored in the utils directory.

  • The entry point of the application is app.js.

  • The public directory contains static assets like index.html and styles.css.

By structuring the code this way, developers can easily locate and work on specific functionalities, promoting a cleaner and more maintainable codebase.


Organising code using clean architecture pattern

Clean Architecture is a software architecture pattern that emphasises separation of concerns and maintaining a clear distinction between business logic, application, and infrastructure layers. Let's consider an example of organising a backend project using Clean Architecture. Here's the folder structure:

my_backend_project/
├── src/
│   ├── app/
│   │   ├── controllers/
│   │   │   ├── user_controller.py
│   │   │   └── post_controller.py
│   │   ├── usecases/
│   │   │   ├── create_user.py
│   │   │   ├── get_users.py
│   │   │   ├── create_post.py
│   │   │   └── get_posts.py
│   │   └── repositories/
│   │       ├── user_repository.py
│   │       └── post_repository.py
│   ├── domain/
│   │   ├── entities/
│   │   │   ├── user.py
│   │   │   └── post.py
│   │   ├── usecases/
│   │   │   ├── create_user.py
│   │   │   └── get_users.py
│   │   ├── interfaces/
│   │   │   ├── user_repository_interface.py
│   │   │   └── post_repository_interface.py
│   │   └── errors/
│   │       ├── validation_error.py
│   │       └── not_found_error.py
│   ├── infrastructure/
│   │   ├── repositories/
│   │   │   ├── user_repository_impl.py
│   │   │   └── post_repository_impl.py
│   │   └── database/
│   │       ├── connection.py
│   │       ├── models/
│   │       │   ├── user_schema.py
│   │       │   └── post_schema.py
│   │       └── migrations/
│   │           ├── user_table_migration.py
│   │           └── post_table_migration.py
│   └── main.py
├── tests/
│   ├── integration/
│   │   ├── test_user_controller.py
│   │   └── test_post_controller.py
│   ├── unit/
│   │   ├── test_create_user.py
│   │   ├── test_get_users.py
│   │   ├── test_user_repository.py
│   │   └── test_post_repository.py
│   └── acceptance/
│       ├── test_create_user.py
│       └── test_get_users.py
├── config/
│   ├── database.py
│   ├── logger.py
│   └── server.py
├── migrations/
│   ├── user_table_migration.py
│   └── post_table_migration.py
└── requirements.txt

In this Python version of the project:

  • The src directory contains the core application code following Clean Architecture principles.

  • The app directory contains the application layer, with controllers and use cases that define application-specific business logic.

  • The domain directory contains the domain layer, including entities, use cases, interfaces, and custom error classes that are independent of any external frameworks or infrastructure.

  • The infrastructure directory houses the infrastructure layer, which includes implementations of repositories and database interactions.

  • The tests directory contains different types of tests organised into integration, unit, and acceptance testing directories.

  • The config directory contains configuration files for the application, such as database and server settings.

  • The migrations directory contains database migration scripts to manage database schema changes.

  • The requirements.txt file lists the project's dependencies.

This folder structure adheres to clean architecture principles by enforcing a clear separation of concerns and dependencies between the different layers of the application. This allows for easy testing, maintainability, and scalability as the project grows.


Clean Architecture with Bounded Context

Using clean architecture with bounded context, the codebase is structured around distinct modules, each representing a specific bounded context within the application. Bounded contexts help define clear boundaries between different parts of the system, allowing teams to work independently and reducing the risk of interference between contexts. Here's an example of organising a Python backend project using Clean Architecture with module structure and bounded context:

my_backend_project/
├── users/
│   ├── src/
│   │   ├── app/
│   │   │   ├── controllers/
│   │   │   │   └── user_controller.py
│   │   │   └── usecases/
│   │   │       └── create_user.py
│   │   └── domain/
│   │       ├── entities/
│   │       │   └── user.py
│   │       ├── usecases/
│   │       │   └── create_user.py
│   │       ├── interfaces/
│   │       │   └── user_repository_interface.py
│   │       └── errors/
│   │           └── validation_error.py
│   └── tests/
│       ├── integration/
│       │   └── test_user_controller.py
│       └── unit/
│           └── test_create_user.py
├── posts/
│   ├── src/
│   │   ├── app/
│   │   │   ├── controllers/
│   │   │   │   └── post_controller.py
│   │   │   └── usecases/
│   │   │       └── create_post.py
│   │   └── domain/
│   │       ├── entities/
│   │       │   └── post.py
│   │       ├── usecases/
│   │       │   └── create_post.py
│   │       ├── interfaces/
│   │       │   └── post_repository_interface.py
│   │       └── errors/
│   │           └── validation_error.py
│   └── tests/
│       ├── integration/
│       │   └── test_post_controller.py
│       └── unit/
│           └── test_create_post.py
├── shared/
│   ├── src/
│   │   ├── domain/
│   │   │   ├── entities/
│   │   │   │   ├── user.py
│   │   │   │   └── post.py
│   │   │   └── errors/
│   │   │       └── not_found_error.py
│   │   └── infrastructure/
│   │       └── database/
│   │           └── connection.py
│   └── tests/
│       └── unit/
│           ├── test_user.py
│           └── test_post.py
└── main.py

In this example:

  • The project is divided into separate modules, each representing a bounded context, such as users and posts.

  • Each module follows the Clean Architecture structure with its src and tests directories.

  • The shared module contains common code that is shared between different bounded contexts, such as shared entities and errors.

  • Each bounded context (users and posts) has its application logic, domain, and test directories, ensuring that the code within a context remains isolated from others.

  • The shared module contains code that can be shared across different contexts but doesn't directly belong to any single context.

This folder structure helps maintain clear boundaries between different bounded contexts, allowing teams to work independently on their respective modules. Additionally, the shared module facilitates code reuse and reduces duplication across contexts.

PreviousAtomic commits in git & it's benefits for software teamsNextGit branching strategies for software teams

Last updated 1 year ago

Was this helpful?