Talks

Seven Habits of Highly Effective Gems

Seven Habits of Highly Effective Gems

by Mat Brown

In the video "Seven Habits of Highly Effective Gems" presented by Mat Brown at RubyConf 2015, the main topic revolves around the principles that can help developers create popular and effective Ruby Gems. Mat Brown emphasizes that while publishing a Ruby Gem is easily achievable, making it appealing and functional for users requires following best practices. The talk is structured around seven key points designed to improve the user and contributor experience.

Key Points Discussed:

  • Quick Start Guides: A simple and concise quick start section is vital for user onboarding. Drawing on the example of the Sinatra gem, Brown highlights how a four-line installation command doesn’t overwhelm new users, making it desirable and effective.

  • Comprehensive Documentation: Effective documentation in the README is necessary. A long and detailed README, echoing the usefulness of Sinatra's documentation, provides a roadmap for users and should be updated in parallel with new features or changes in the codebase.

  • Semantic Versioning: Brown stresses the importance of semantic versioning to communicate changes clearly, allowing users to safely upgrade gems without unforeseen issues. A version number is split into three parts: major, minor, and patch, indicating the nature of updates.

  • Dependency Management: Managing dependencies thoughtfully prevents locking users into specific versions. Loose version constraints encourage user-friendly upgrades while ensuring compatibility across different environments.

  • Testing and Contribution: A seamless testing experience is crucial for encouraging contributions. Tools like Bundler and Travis can help automate testing across multiple Ruby versions while providing contributors with clear style guidelines through tools like RuboCop.

  • User Interface Design: Crafting a public interface that aligns with user expectations is emphasized. Developers should ensure that users can access essential functionality without complications, indirectly affecting how often their gem is used.

  • Multiple APIs: Brown advocates for creating both high-level and low-level APIs to cater to varied user needs, allowing flexibility and robustness in how the gem can be leveraged.

Conclusions and Takeaways:

Mat Brown concludes with actionable insights for developers:
- Innovate with concise quick setup instructions.
- Maintain exhaustive, user-centric documentation and inline comments.
- Consistently apply semantic versioning principles.
- Ensure backward compatibility and manage dependencies carefully.
- Encourage community contributions through an easy-to-run test suite and comprehensive guidelines.
- Foster a well-defined user interface, enabling users to utilize gems effectively. By integrating these habits, developers can enhance the reach and success of their Ruby Gems, ultimately contributing significantly to the open-source community.

00:00:14.530 All right, what's up everyone? How's it going? So, who among you has ever released a Ruby gem? Okay, good, I like it. Now, who has released a popular gem with tons of contributors, users, and lots of love? Okay, all right. So, today we want to get from Group 1 to Group 2, and everyone is welcome to join us. Before we get started, a quick introduction: my name is Mat Brown, and if you want to tweet insults at me, that's fine. I live in Brooklyn, and I work for Genius, a platform for annotations. One time, John Resig used our platform, and it was really cool. Here's my talk.
00:00:41.150 We will discuss seven specific things that will make your gem more appealing and ensure your users are happy. Of course, this is a Ruby conference, so we'll talk about how to get users up and running quickly. We'll cover writing good documentation, changing your gem without causing pain to users' applications, coexisting with users' application environments, and how abstractions can lock people in and how to prevent that.
00:01:08.540 We'll also discuss making it easier on your contributors by providing an easy-to-run test suite and giving them automatic feedback on their contributions. Let's start with Quick Start. Take Sinatra as an example. It’s a fantastic gem; arguably the best micro-framework out there. The top of the Sinatra README features a quick start that is only four lines long.
00:01:49.370 This four-line setup includes the Ruby installation command, which doesn’t even count as code. You type one line into your shell, and you're good to go. This is what we should all aspire to. Everyone expects a quick start this easy. Programmers aren't asking for it because they aren't smart or are lazy; they want to spend their time building beautiful, functional, wonderful products instead of installing and configuring things.
00:02:20.640 So, having a really short and effective quick start is actually a feature of your code, not just your README. You can't create a short quick start unless your gem allows it. So how do we achieve that? Think about how we usually write a gem. When working on an application, we might have some code that we think should be open sourced, believing it would be useful to others.
00:02:46.400 We take some code, create a directory for our gem, and probably point to the local version of the gem in our application's Gemfile. While developing it, we might write some tests and documentation, then go back to our application to check compatibility. However, I never feel what it's like to use the gem from scratch in a fresh application. Something that has worked well for me is creating an example app.
00:03:32.160 It doesn’t have to be a Rails app; just something that makes sense for the problem you're solving. Consider how it feels if I wanted to use this gem for a plausible use case. I always find at least one pain point that I can solve to make getting started using this technique easier.
00:04:05.040 The key is to put yourself in the position of a new user. Once they start using it, they will want to learn more and easily find everything your gem has to offer. That’s where documentation comes in. Returning to Sinatra, not only does it have a great quick start, but scrolling down shows that the README is incredibly detailed.
00:04:29.230 A long README is very beneficial because there's no better place for documentation than the README itself. It's prominently displayed on GitHub. Also, when you generate code documentation, it is often included with the gem, allowing access without the internet. The README is crucial for laying out a map of what your gem is capable of.
00:05:09.170 To ensure that it accomplishes this, I recommend making the README part of your development process, just like writing tests. You wouldn’t submit a pull request or even make a commit containing new code without including new tests.
00:05:39.440 You can extend this further: whenever you add something new to your application, also add corresponding information in the README. Think about how you're going to explain this to your users before you lay down any tests or code.
00:06:07.880 Every feature of your gem should appear in the README, making it searchable so users can navigate it effectively. However, keep in mind that the README cannot be exhaustive. It can't cover every method or option; that’s where inline code documentation becomes essential.
00:06:53.260 For example, the Addressable gem uses Yard, a documentation tool, to structure Ruby code documentation more effectively than the built-in Ruby documentation. Yard enhances the potential for communication about how our code works. It provides a way to specify expectations about arguments and return types, generating clear documentation that communicates these requirements.
00:07:43.770 Yard includes various tags, such as the API tag, allowing us to specify the visibility of methods, marking deprecated methods with a single line. This tool ensures that our documentation is accurate and can even give more authority to the documentation than the code itself. Additionally, Ruby Doc Info hosts free documentation for Ruby projects, enhancing the usability of gems by automatically pulling documentation from your code and it’s quite beneficial.
00:08:51.600 However, we must also ensure that our gems do not cause unpleasant surprises. Users should not fear potential issues from upgrading to a new version of your gem. A primary means of conveying versioning information is by using semantic versioning, which divides version numbers into three parts.
00:09:20.620 The basic idea is that bumping the patch level signifies bug fixes that do not change any outward-facing behavior, meaning users can safely upgrade without worry. Bumping the minor version indicates that something new has been added without breaking existing functionality. Finally, the major version signifies a breaking change that requires a careful approach to upgrading.
00:09:53.470 As developers, we sometimes focus too much on our improvements and not enough on our users' experience. Aiming to design a beautiful interface might lead to breaking existing ones, causing unnecessary pain for your users. We can learn from JavaScript, where the community chose to always support old versions of existing code in future versions, introducing a new keyword rather than breaking the past.
00:10:35.420 This way, the JavaScript community ensures that all previous versions of code continue to work, supporting users who have invested in older interfaces. In thinking about the environment in which our gems exist, we need to play nice with other libraries.
00:11:16.460 Most gems have at least one dependency, which is Ruby itself, and they may also depend on other gems, making it critical to manage these dependencies thoughtfully. In our gemspec, we should specify version dependencies but aim for loose constraints to avoid locking users into specific versions.
00:11:50.900 Instead of demanding an exact version of a library like Active Support, we can use a broader versioning strategy that allows for safe upgrades, thus creating a better experience for users who may not be able or willing to stay on the latest versions.
00:12:31.770 Employing tools like Gymnasium can help audit your gems’ dependencies and notify you when they are out of date, making it easier to ensure compatibility moving forward. However, even if you say you support many versions, it’s crucial that you confirm that your gem operates correctly with all of them.
00:13:14.070 You will want to run tests against all the versions of Ruby and other core dependencies your gem claims to support. Travis is an excellent tool for this, providing seamless integration with GitHub, automatic testing of your code against various Ruby versions and forks, and ensuring your gem works across different environments.
00:14:47.190 Furthermore, maintaining robust test coverage across different versions is crucial, and the Appraisal gem helps you achieve that by allowing you to define a variety of gem dependency configurations to be tested automatically. This ensures that any changes or updates to your gem do not inadvertently break compatibility.
00:15:27.990 Next, let's consider the user experience in social networks, like Twitter and Facebook, which have great gems developed for interacting with their APIs. The public interface we create in our gems is what users expect to work with, and we should ensure it reflects the actual functionality.
00:15:56.550 For instance, while the Twitter gem provides a method to update tweets, if a new feature is introduced, such as 'poking,' it might not yet be supported. Users might want to access lower-level methods, yet the gem's private methods must remain hidden from users to avoid any inconsistency.
00:16:41.460 Alternatively, gems can be designed with a lower-level API as a stable foundation, building higher-level functionality on top of it. This layered architecture allows you to provide flexibility and robustness to your users, enabling them to create more complex calls when needed.
00:17:12.430 The quick start guides your users just as the test suite guides your contributors. If contributors cannot run the tests easily, they will likely become frustrated and not contribute back to your project. Therefore, ensuring that your test suite is easy to run is vital for encouraging contributions.
00:17:54.410 Utilizing tools like Bundler can greatly simplify the testing process, allowing you to manage testing dependencies effectively. If you rely on external services such as PostgreSQL, consider using Vagrant, which helps provide a consistent test environment for potential contributors.
00:19:01.130 However, being able to run tests is only one part; you also want to ensure contributors can write code that adheres to your style guidelines. Tools like RuboCop can help in setting style rules, providing automatic linting, and giving contributors non-confrontational feedback about code style.
00:19:40.750 By integrating RuboCop into your project, you can remove personal biases regarding code style from the feedback process. This helps maintain a consistent style across your project without subjective judgments from the maintainers. Just remember that setting up clear guidelines and using such automation tools can greatly enhance the quality and cooperation around contributions.
00:20:44.200 In conclusion, recap the key points: write a gem with a really short and effective quick start; develop an extensive README; utilize tools like Yard for code documentation; avoid breaking changes whenever possible; leverage semantic versioning to communicate effectively with your users.
00:21:56.280 Ensure that your gem supports older versions whenever applicable, keeping an eye on updates through tools like Gymnasium, and continuously run tests across various supported versions utilizing Travis. Remember to avoid locking users into specific abstractions whenever possible.
00:23:18.160 Consider providing both high-level and low-level APIs to accommodate different use cases. Make it simpler for contributors to run your test suite by leveraging tools like Bundler and Vagrant. Finally, use RuboCop to ensure non-confrontational feedback on style issues.
00:25:04.360 Now, let’s address some questions. The first question is regarding the use of RuboCop. I appreciate the feedback. Another question is about creating a contributing document, which is indeed a great idea. You can create a file named CONTRIBUTING.md, and GitHub will automatically link to it in pull request submission forms.
00:25:59.360 When it comes to the choice of license, I would suggest MIT, as it's a very standard choice that most developers are familiar with. Avoid multiple license types in your gems to prevent confusion. If you are publicizing a gem, consider platforms like Ruby Flow, Hacker News, and subreddit communities related to Ruby.
00:27:35.280 When it comes to versioning, I recommend starting with a version like 0.0.1 during initial development. After that, when you are ready to go live, feel free to release it as 1.0.0. If you support newer versions like Rails 5, assess whether it's a patch level or minor upgrade based on changes made—this helps in maintaining clarity for users.
00:28:53.240 Finally, if you decide to use someone else's namespace in your gem, maintain that convention. Underscored names convention is preferred for Ruby files, whereas dashes are typically better suited for defining namespacing in gem files.
00:30:25.760 Indeed, it can be a bit wild west regarding naming conventions, so tread carefully. If you must test compatibility with different dependencies, tools like Appraisal can help list the versions to check against and collect results in a methodical way.
00:30:47.240 When integrating your gem with Rails, it is genuine to create a full Rails app just for testing purposes, especially if there are view-level helpers involved, as that simulates the actual environment.
00:31:00.540 Above all, remember that keeping your gem flexible, well-documented, and easy to contribute to is essential for long-term use and community health.