Skip to content

Instantly share code, notes, and snippets.

@thatkookooguy
Last active June 30, 2024 08:27
Show Gist options
  • Save thatkookooguy/e8b2e32abe9818b14246fc3bb4686147 to your computer and use it in GitHub Desktop.
Save thatkookooguy/e8b2e32abe9818b14246fc3bb4686147 to your computer and use it in GitHub Desktop.

Lesson: Benefits of Small Commits (with unit-tests included)

Introduction

In this lesson, we'll explore the benefits of working with small commits and maintaining functional code at every step. We'll use a simple TypeScript calculator project and add basic operations one by one, pairing each change with its corresponding unit test using Jest. After completing the initial development, we'll introduce git bisect to demonstrate how small commits make debugging easier.

The calculator project should reflect the same stack we're using: Typescript, and jest. The code should be added into a src folder which can be built using typescipt, and the scripts in package.json should also reflect the same scripts we usually work with in our repos (build, start, test, and test:watch)

Ofc this example is minimal to show the git changes more clearly and our real application code usually is harder to apply these techniques, but the examples in this lesson should give you a starting point on how to approach this.

Before development

Creating sub-tasks in Jira effectively can help streamline your development process and promote small PRs. Here are some best practices for sub-tasks to help with that.

Break Down Stories/Tasks into Smaller Sub-Tasks

  • Identify Components: Break the main task into smaller, manageable components that can be individually developed and tested.
  • Clear Definition of Done: Each sub-task should have a clear definition of done to ensure it is completed properly before moving on.
  • Logical Progression: Ensure sub-tasks follow a logical sequence that mirrors the development workflow.

Set Clear Goals for Each Sub-Task

  • Single Responsibility Principle: Each sub-task should focus on a single aspect or feature. Avoid combining multiple changes in one sub-task.
  • Detailed Descriptions: Provide detailed descriptions, acceptance criteria, and relevant documentation for each sub-task to avoid ambiguity.

Time-box Sub-Tasks

  • Effort Estimation: Estimate the effort required for each sub-task and ensure it’s small enough to complete in a short period, ideally within a day or two.
  • Review and Adjust: Regularly review and adjust the scope of sub-tasks to keep them small and manageable.

Prioritize and Sequence Sub-Tasks

  • Dependencies: Identify and document any dependencies between sub-tasks. Ensure they are prioritized and sequenced accordingly.
  • Critical Path: Focus on the critical path first—sub-tasks that unblock other work should be prioritized.

Collaborate and Communicate:

  • Team Discussions: Involve the team in the breakdown process to ensure everyone understands the scope and goals of each sub-task.
  • Regular Updates: Maintain open communication about the progress of sub-tasks. Use comments and status updates in Jira to keep everyone informed.

Integrate with Development Workflow:

  • Branching Strategy: Align your branching strategy with Jira sub-tasks. Create separate branches for each sub-task to facilitate small, focused PRs.

Review and Refine:

  • Retrospectives: Conduct regular retrospectives to review how well the sub-task breakdown is working and identify areas for improvement.
  • Feedback Loop: Encourage feedback from the team on the size and scope of sub-tasks to continually refine the process.

Summary: BASICS for Creating Effective Sub-Tasks

BASICS is an easy-to-remember acronym for creating effective sub-tasks in Jira to promote small PRs.

Letter Step Name Step Description
B Break Down Tasks Divide main tasks into manageable sub-tasks.
A Assign Clear Goals Ensure each sub-task has a specific goal and detailed description.
S Set Time-Box Keep sub-tasks small and quickly completable.
I Identify Dependencies Document and prioritize dependencies.
C Collaborate Discuss and update progress with the team regularly.
S Sequence and Integrate Align branching strategy with sub-ttasks and use PR templates.

By following the BASICS framework, you can streamline your development process and promote small, manageable PRs.

Developing with Small Commits and Unit Tests

Benefits of small commits

  1. Isolated Changes:
    • Each commit introduces a small, focused change.
    • Easier to review and understand.
  2. Immediate Feedback:
    • Each commit includes tests, ensuring that new code works as expected.
    • Issues are caught early.
  3. Easy Reverts: If a bug is introduced, you can revert specific commits without losing unrelated work.
  4. Clean History: A clear and understandable commit history makes it easier to track changes and debug issues.

Some techniques and best practices

  • Planning in Advance: As discussed in the part 02, breaking down features into smaller, manageable sub-tasks before starting development, makes each sub-task correspond to a single branch and PR.

  • Create Branches Early: As soon as you finish a specific sub-task, create a separate branch for the next sub-task or sub-feature. This helps in isolating changes and makes it easier to create focused PRs. After the 1st branch is merged to develop, you should be able to rebase the 2nd branch onto the develop branch and open the next PR when you finish that branch.

  • Single Responsibility:

    1. Each commit should have a single responsibility, making it easier to understand and review.
    2. Avoid mixing different types of changes (e.g., feature implementation, refactoring, and bug fixes) in a single commit.
  • Descriptive Commit Messages:

    1. Use clear and descriptive commit messages that summarize the purpose of the commit.
    2. Follow a consistent commit message format, such as the semantic commit structure, to maintain clarity and organization in the commit history.
  • Test-Driven Development: Write tests alongside code changes to ensure that each commit is functional and verifiable.

  • Commit Empty New Files with Basic Wrappers for new files: Start by committing empty files with basic class or function wrappers and test descriptions. This approach makes it easier to cherry-pick commits and reorder changes as needed.

    Example:

    // src/calculator.ts
    export class Calculator {
      add(a: number, b: number) {
      }
    
      subtract(a: number, b: number) {
      }
    
      multiply(a: number, b: number) {
      }
    
      divide(a: number, b: number) {
      }
    }

    Using Jest test.todo for Planned Tests

    Jest supports the test.todo feature, allowing you to write placeholders for tests that you plan to implement later. This is useful for outlining the tests in advance and ensuring that the structure of your test suite is ready.

    Example:

    // tests/calculator.test.ts
    import { Calculator } from '../src/calculator';
    
    describe('Calculator', () => {
      test.todo('adds 2 + 3 to equal 5');
      test.todo('subtracts 5 - 3 to equal 2');
      test.todo('multiplies 2 * 3 to equal 6');
      test.todo('divides 6 / 3 to equal 2');
    });
  • After files are created:

    1. Pairing the implementation of a method with its corresponding test in the same commit ensures that each commit is both functional and verifiable.
    2. This practice reinforces the habit of writing tests alongside code changes, promoting a test-driven development (TDD) approach.
    3. if this still creates a big PR, implementation and tests can be splitted into 2 different commits for a single metho, but should generally be sequencial commits in order to verify the new code ASAP in the same commit sequence
@thatkookooguy
Copy link
Author

  • warning about example (intro)
  • talk about jira subtasks
  • writing code correctly from the start
  • talk about hunks, amend and ways to change things up after writing code
  • talk about git bisect

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment