DEV Community

Peter Wan
Peter Wan

Posted on

Tips on Improving an Open Source Project

Hello there!

I just wanted to leave some notes on what I think are some best practices to follow when creating and maintaining an open source project.

I have implemented these best practices on my GitHub repository for my command-line tool, gimme_readme, which allows one to send multiple source files to an LLM easily, and get a quick response on what those source files are doing.

GitHub logo peterdanwan / gimme_readme

gimme_readme is a command-line tool powered by AI that generates a comprehensive README.md file for your project. It analyzes multiple source code files at once, providing concise explanations of each file's purpose, functionality, and key components, all in a single, easy-to-read document.

gimme_readme

gimme_readme is a command-line tool powered by AI that generates a comprehensive README.md file for your project. It analyzes multiple source code files at once, providing concise explanations of each file's purpose, functionality, and key components, all in a single, easy-to-read document.

gimme_readme-0.1-demo-revised

See our 0.1 Release Demo!

Table of Contents

  1. Getting Started
  2. Usage
  3. Example Usage
  4. Supported Models by Providers
  5. Contributing
  6. Testing Locally
  7. Author

1. Getting Started

To get started with gimme_readme, follow these steps:

  1. Install the latest version of Node.js for your operating system.

  2. Run the following command to install gimme_readme globally:

    npm i -g gimme_readme
    Enter fullscreen mode Exit fullscreen mode

    NOTE: MAC/LINUX users may need to run sudo npm i -g gimme_readme

  3. Generate a configuration file by running in any folder you'd like:

    gr-ai -c
    Enter fullscreen mode Exit fullscreen mode

    This command creates a .gimme_readme_config file in your home directory. Do not move this file from this location.

  4. Open the .gimme_readme_config file and add your APIโ€ฆ

My quick advice is to set everything I'm about to mention as soon as possible! It'll make your development experience as well as the development experience of others as seamless as possible.

Start with a License

Whether you want your project to be worked on by others or not, you should look into adding a license to your work. Your license will describe what others can or cannot do with your code, so I would highly suggest you think about this before creating your repository.

That said, I am not a lawyer, so I cannot disclose information on what's the best license for your use-case or provide any legal advice.

What I can say is that, I would start by reading the documentation that GitHub has provided here to set up a License for your project.

Add a main README.md file and have it link to other documentation when appropriate

Your README.md should provide a clear overview of your project and getting started instructions. For detailed topics that require in-depth explanation, create separate documentation files and link to them from your main README.md.

Recently, I have added a section to my README.md that links to the License I mentioned earlier. This would be a prime example of when you'd want to provide a link to something instead of explaining it directly in your README.md.

Add a CONTRIBUTING.md file & CODE_OF_CONDUCT.md

Since I have been invested with learning more about Open Source Development, I have come across two common files that are usually apparent in large, open-source projects:

  1. CONTRIBUTING.md
  2. CODE_OF_CONDUCT.md

These files appear to be fairly common with large projects and have a very well defined purpose (you can infer what they're about just from the names!).

You'll note that both of these files are quite lengthy, but at the same time focused. Having these files can help minimize the bloat of your main README.md file, which should explain the scope of your project, and direct others to appropriate resources when necessary.

Imagine you're a maintainer for a project and someone asks you for the steps on how to contribute.

If all your instructions were in your README.md file, you'd have to say, "Look at my README.md file and look for the contributing section".

In comparison, if you have a dedicated file to contributing, you can just say, "Please read CONTRIBUTING.md". That in of itself is a major time saver (believe it or not).

With regards to the CODE_OF_CONDUCT.md, it's nice to have a document that explains your expectations of others who interact with your project. Having a document like this serves as a safeguard for you to enforce rules of conduct on your project, and hopefully, gets others to behave properly.

As such, I would highly recommend creating these files to make your experience as a creator/maintainer of a project seamless.

I don't expect you all to re-invent the wheel when making a CONTRIBUTING.md file or CODE_OF_CONDUCT.md file.

My Professor, David Humphrey shared with my class a useful site which generates these files for you.

That is: https://generator.contributing.md/.

After discovering this site, I made a CONTRIBUTING.md file and CODE_OF_CONDUCT.md file and began simplifying my main README.md file.

Below is a screenshot that shows how much clutter was removed from my main README.md file now that I have dedicated files on how to contribute and how to conduct oneself in my repository:

condensed-readme

For casual users of my tool, I believe this to be a lot less intimidating when trying to get the gist of my repository.

For people looking to develop, they'll be able to quickly scan my main README.md and know where to find guidelines on how to contribute and conduct themselves.

Now, I want to talk about adding static analysis tools to my project.

Add static analysis tools

Very early on in my project, I added static analysis tools which are essential utilities that analyze code without actually running it. These tools help maintain code quality, consistency, and catch potential issues early in development. Below, I have listed different types of static analysis tools and examples of these tools that I've used in gimme_readme.

NOTE: that since static analysis tools are built to analyze your code, there will be some folders or files that you do not want them to analyze (to save time in processing). I myself, had to make recent changes on what to ignore to speed up my development.

Linters (ESLint)

Linters analyze your code for potential errors, suspicious patterns, and style violations. ESLint helps enforce coding standards and catch bugs before they make it into production. For example, it can:

  1. Detect unused variables
  2. Find syntax errors
  3. Enforce consistent code style
  4. Identify potential runtime errors

Linters will help you spot potential problem areas when others contribute to your repo. They will often help you spot unneeded code (unused variables), and buggy code.

Below, is my Eslint config file, eslint.config.mjs with explanations on what it's doing:

// Import predefined global variables for different environments (browser, node, jest, etc.)
import globals from 'globals';
// Import ESLint's new flat config for JavaScript (includes recommended rules)
import pluginJs from '@eslint/js';

export default [
  // First config object: Define file patterns and module settings
  { 
    files: ['**/*.js'],           // Apply to all JavaScript files
    languageOptions: { 
      sourceType: 'module'        // Treat files as ES modules (enables import/export)
    } 
  },

  // Include ESLint's recommended ruleset
  // This adds common error-catching rules and best practices
  pluginJs.configs.recommended,

  // Configure language features and global variables
  {
    languageOptions: {
      ecmaVersion: 'latest',      // Use the latest ECMAScript features
      globals: {
        ...globals.commonjs,      // Add CommonJS globals (require, module, etc.)
        ...globals.es2021,        // Add ES2021 globals
        ...globals.node,          // Add Node.js globals
        ...globals.jest,          // Add Jest testing globals
      },
    },
  },

  // Define directories to ignore during linting
  {
    ignores: [
      'node_modules/**',          // Third-party dependencies
      '_examples/**',             // Example code directory
      '.husky/**',                // Husky git hooks
      '.git/**',                  // Git internals
      'coverage/**',              // Test coverage reports
    ],
  },
];
Enter fullscreen mode Exit fullscreen mode

Formatters (Prettier)

Formatters automatically style your code according to predefined rules. Prettier handles all the formatting heavy-lifting, ensuring consistent:

  1. Indentation
  2. Line length
  3. Quote usage
  4. Comma placement

Formatters are particularly helpful to ensure that if multiple people are contributing to your repository, the code "reads the same", since everyone will have their code formatted correctly, using your style guide.

Below is my .prettierrc file, that defines the rules I wish to have with regards to formatting my code. Prettier will look at this file to understand the rules for formatting, and check if my source code files abide by these rules. NOTE: that the comments aren't valid in JSON, but I'm putting them here to explain what they do.

// .prettierrc
{
  // Controls whether parentheses are always added around a single arrow function parameter
  "arrowParens": "always",  // Example: (x) => x  vs  x => x

  // Adds spaces between brackets in object literals
  "bracketSpacing": true,   // Example: { foo: bar }  vs  {foo: bar}

  // Controls how Prettier formats quoted code embedded in the file
  "embeddedLanguageFormatting": "auto",  // Automatically formats code inside template literals

  // Line ending style (linefeed only)
  "endOfLine": "lf",  // Uses \n instead of \r\n (Windows style)

  // Whether to insert a special @format marker at the top of files
  "insertPragma": false,  // Won't add @format marker to formatted files

  // Controls how markdown text is wrapped
  "proseWrap": "preserve",  // Doesn't change wrapping of markdown text

  // Whether to only format files with a special @format marker
  "requirePragma": false,  // Formats all files, not just those with @format marker

  // Use single quotes instead of double quotes
  "singleQuote": true,    // Example: 'hello'  vs  "hello"

  // Number of spaces per indentation level
  "tabWidth": 2,          // Uses 2 spaces for each indent level

  // Adds trailing commas where valid in ES5
  "trailingComma": "es5", // Example: { foo: bar, }  vs  { foo: bar }

  // Whether to use tabs instead of spaces
  "useTabs": false,       // Uses spaces for indentation

  // Maximum line length before wrapping
  "printWidth": 100       // Wraps code at 100 characters
}
Enter fullscreen mode Exit fullscreen mode

With Prettier, there is a concept of an ignore file, which will be used to tell Prettier which files to avoid when formatting. This file is the .prettierignore file, and mine is shown below:

# Generated files
node_modules/
package.json
package-lock.json
_examples/
.husky/
Enter fullscreen mode Exit fullscreen mode

Pre-commit Hooks (Husky)

Pre-commit hooks are perhaps my favourite static analysis tool since they can ensure that your other static analysis tools are being used.

With regards to git, a pre-commit hook runs certain actions prior to your commit.

As I explained earlier, we have other static analysis tools such as a linter or formatter - however, there's _nothing enforcing a contributor from linting or formatting their code properly.

If you set up a pre-commit hook, you can enforce that any changes made to your repository (even locally on another user's machine), are formatted and linted properly.

I use Husky to write a very simple file that lets me describe the exact steps I want to run before someone commits a change to my repository. This file is found within the .husky folder, and is called "pre-commit". The content is shown below:

# .husky/pre-commit
npm run format
npm run lint
npm run test
Enter fullscreen mode Exit fullscreen mode

By looking at this file, you'll see that husky runs the following before a commit:

  1. a format script
  2. a lint script
  3. a test script

These npm scripts are defined in my package.json file:

...
  "scripts": {
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js -c jest.config.js --runInBand",
    "lint": "eslint --config eslint.config.mjs \"./src/**/*.js\" \"tests/**/*.js\"",
    "format": "prettier --write \"./src/**/*.js\" \"tests/**/*.js\"",
    "coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js -c jest.config.js --runInBand --coverage",
    "prepare": "husky"
  },
...
Enter fullscreen mode Exit fullscreen mode

Husky runs these commands for you before you commit, but users can also run these scripts directly into the command-line themselves.

The last thing I wanted to talk about was Editor Integration.

Editor Specific Integration

To make development smoother, I've included VSCode configuration files:

.vscode/extensions.json: Suggests recommended extensions for the project

{
  "recommendations": [
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "streetsidesoftware.code-spell-checker"
  ]
}
Enter fullscreen mode Exit fullscreen mode

.vscode/settings.json: Ensures consistent editor settings across the team

{
  "editor.insertSpaces": true,
  "editor.tabSize": 2,
  "editor.detectIndentation": false,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll": "explicit"
  },
  "files.eol": "\n",
  "files.insertFinalNewline": true
}
Enter fullscreen mode Exit fullscreen mode

.vscode/javascript.code-snippets: lets us define shortcuts that will type out repetitive pieces of code with a few characters. I made my own snippet which will print out a file path like // src/ai/ai.js in a JavaScript file when the user writes rpath in that file and presses "Enter".

{
  "Insert Current File Path Comment": {
    "prefix": "rpath",
    "body": ["// ${RELATIVE_FILEPATH/[\\\\]/\\//g}"],
    "description": "Inserts a comment with the relative path of the current file"
  }
}
Enter fullscreen mode Exit fullscreen mode

These files help new contributors get set up quickly with the right tools and settings.

Conclusion

Implementing these best practices early in your open source project sets a strong foundation for both you and your future contributors. From establishing clear licensing and documentation structure to setting up robust static analysis tools, each element plays a crucial role in creating a professional and maintainable codebase.

While it might seem like a lot of upfront work, having proper licensing, well-organized documentation (README.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md), and automated tools (ESLint, Prettier, Husky) will save countless hours of manual work and reduce potential friction in the development process. These practices not only make your project more approachable for contributors but also help maintain high code quality as your project grows.

Remember: it's much easier to establish these practices at the start than to retrofit them into an existing project. Whether you're building a small tool or planning for a larger open source project, taking the time to implement these configurations early will pay dividends in the long run.

Just take a look at how many commits I needed to do to fix my project (if you set this up early, there will be less work in the future, I promise!):

before-rebasing

It was too much for me to stare at, so I decided to squash my commits into one like so:

after-rebasing

With that, that's pretty much everything I wanted to cover - organize your documents early and use static analysis tools. Happy programming!

Top comments (0)