(upbeat music) - So the title of this presentation is "How to Make a Gem of a Gem." My name is Justin, you may know it. That's my email address, and you can find me on the internet by my last name, Searls. I'm, Twitter and GitHub
and LinkedIn and RubyGems. I have so far, written 39
RubyGems over the years, and my goal today is just to share some lessons that I've learned over time. And in fact I've already lied to you. I've actually written 40 RubyGems, but one of the ones I'm
gonna talk about today, I haven't announced yet, so stay tuned. And, I've lied again, because
we've written 41 RubyGems, 'cause one of them, we're gonna do on stage
together right now, and we're gonna release it live. So that's right. Today, you become a RubyGem maintainer. If you're here in this room or
if you're watching on video, 'cause I just made that up. And the goal of this presentation today is we're gonna answer two questions. One, the first talk is really like, "how do I make a gem?" And, the second question is like, "How do I make it good?" And we're gonna talk
about both those things. And it's just not as hard as it might look if you've never written a RubyGem before. So part one, how do I make a gem? I think this mic got turned on. So the right way to make a
gem is using Bundler, here. Bundler comes with a great
command called Bundle Gem, and you can just type in the gem name that you have, after the fact. And, here I am creating
a gem called "bored". And so, it's kind of like
an interactive fiction. It kind of sets up like a text adventure saying, "hey, do you
wanna generate tests," and you can pick out your test framework. So I'm here using minitest. "Do you wanna have a
continuous integration "set up for your gem?" And so, you can choose between
all these different options, and I like to use GitHub actions lately. It's very fast, it's all your code is in one place, it's easy to use. And then, it asks, "do you wanna license it
as open source using MIT?" Most gems are licensed MIT, so, yeah, let's do that. "Do you wanna have a code of conduct "in the gems that you generate?" Here's what a code of conduct
is and how to enforce it. "Yes or no?" Sure, let's have it. And, "Do you want a changelog?" This is a text file that
tells you each version, what changed in your gem. You can read about it at
Olivier's awesome site, keepachangelog.com. "Yes or no?" Yeah, I wanna have a changelog. And then, "Do you wanna add
a code linter and formatter?" So your options here are
RuboCop and Standard, and I actually, this is the first thing I
ever contributed to Bundler, it was this feature. I was excited to do it, 'cause we maintain
standard at Test Double, so I'm gonna use Standard. And then it initializes a Git repo here. And then, the next thing
I do is I just run, git commit, Bundle, gem board. Whenever I generate a new
file, a new project, excuse me, I like to commit it right away so that way anything that I change I know is something I did. So let's go ahead and commit it. All right, do we did it. We created the new gem. So let's run, bundle install. And there's these warnings, now an error saying that
the gem spec is invalid because there's all these to-dos in there. So we don't actually
have a working gem yet. So let's pop into the gem spec. And when we do, we can zoom in and you can see there's all
these sort of like to-do items. We just have to work
through each of those. So we're gonna write a description. We don't need that second description. We're gonna put it in the home pages, just the Github page. We don't need that thing. We can set that and we can
set the source code URL, and then the change link URL and now we've gotten
all these to do's done so we can commit that we fixed the gemspec and there we go. We can run, bundle, install again, and that's now gonna bundle
and install everything cleanly. So that's great. So now we can run rake and that's gonna run
our tests and then lint, and you can see that the tests failed and here's the default test, test_bored. We're gonna pop into that file here. And you can see it's got the second test and just like test to make
sure that it's doing something. We'll just throw that away. 'Cause I just wanna get to a passing build as fast as possible. Great, so now we're passing. A great way to make your build
pass is to delete the tests. So we did it, we made a
gem that does nothing. Congratulations. What this gem is gonna do is it's gonna call this free
API called the bored API. So we're gonna write a quick test. So test that it just returns an activity. The public API is gonna be Bored.now. And we're just gonna start that this gives us some kind of object that kind of like has all
these same properties, like price and description
and that stuff on it. Run rake again, and now we're gonna see that that fails because there's no Bored.now
method on that module. And this is not a test
driven development talk. So I'm not gonna ping pong back and forth and show every single step. We're just gonna dive in and
like write all this code. This is a generated file
that comes with the gem. It says your code goes here. So let's just follow instructions and wipe away that comment. And then we're gonna write
a module method called now, and we're gonna use net HTTP, passing the domain and a path. And then we're gonna wrap that in a call to JSON.parse. And so we're gonna take that string and turn it into some a JSON e-hash. And then we're gonna
create a new custom object called an activity. And we're gonna translate that JSON into like a normal, Ruby-ish object. Now activity needs to exist too. So we're gonna define that
class by using Struct. And then we're gonna create all these different
property types in here. Turn on keyword initialization. 'Cause it makes it a
little bit easier to use. And the last but not least, we got to go over require
those two things that we use. So we got to require net HTTP and JSON. And those are both part
of the standard lib. So we don't actually have to require any additional gems or anything. So we zoom way out. And this is like our whole
first gem implementation. Hopefully it passes the test. Okay, cool. And so now we're passing
and now we're ready to go. We can ship it. So we're gonna go ahead and
just update the version. So by default, it gives you a 0.1.0, but I'm a big fan of
lowering expectations. So I always start at 0.0.1. They I run bundle, which is gonna update the gem file.lock. And so that won't cause
a need to git churn. Then I changed the change
log and I say, okay, so update the version here and say, what is in this version? Just this one method. All right, so I can commit version 0.0.1. And then I run rake-T. Rake-T is a cool command that gives you all of the
rake tasks that are defined. So I'm gonna run them here and see, oh, I've got this cool rake release. It's kind of like, does everything. It makes sure you're all bundled. It generates the gem, it packages it up. It pushes all the, like the git commit and creates a git tag. And then it finally releases
it to RubyGems all in one go. Real, real, simple, easy to use. So we're gonna run rake release here. And then it's asking for
my MFA code for Ruby gems because I've got 2FA set up. So here's the code and then
boom, push the Ruby gems. So we did it. So this gem is really up on Ruby gems. That means you're a gem author now. And so you're also responsible for the maintenance of this gem. And whenever I put up a new gem, I always like look up at the GitHub site and I just revel in my work and I zoom in and I wait, I
wait for it and then boom. It happened, our first issue. (audience laughing) So somebody's got a complaint
and it's my partner Todd. And he's like, this doesn't
make any sense as an API. It makes a lot more sense
as a command line interface. Like maybe we should have like
a message of the day pop up and tell you what to do instead of being on your computer. And so how do you write a computer, a command line interface? Actually like the bundler gem tools and the right tasks make this pretty easy. There's just some
additional kind of plumbing and it looks arcane. So let's walk through it. So we're gonna, pop back
into the gem spec again. And if you look here, this spec.files thing is essentially just you
shelling out to Git, to look for all of the files
that are tracked by Git. And then there's... We specify where our binaries
live in the exe folder. And then we go and grab
all those executables, those things tracked by Git. Again, we're just focused
on the plumbing here and we can see that that exe
directory doesn't exist yet. So we're gonna make it,
we're gonna touch a file. We're gonna make it executable, and then we're gonna go and start writing this little script. So I need a shebang here to say like, hey, I'm written a Ruby code. The load path stuff is normally
handled for us by RubyGems. But because we're inside the gem, we actually have to manually
load that live directory, which will allow us to
require that bored file. And then this doesn't exist yet. But I know I'm not some
sort of like CLI object. I'm gonna pass it, RVU,
the argument vector. And that's all of the kind
of command line parameters that people might type and then call run. None of this exists, but I just wanna kind of
get this done on paper and then I'm gonna actually commit it now, even though it's like a work in progress, because like I wanna actually see that when I run a rake install, which is gonna package up the gem and then install it locally, that I should be able to
run the bored command. So when I run bored, I am elated to see this error message because it shows that
it's actually successfully running my script. And so I've got all the
plumbing stuff worked out and the next step is to
actually make it work. So the first thing we got to do is got to go back into our lib board, which is kind of the
entry point for our gem. We're just gonna require
relative board CLI thing. Again, this file doesn't exist. So we got to go make it. And then we're gonna go
create the CLI class. And as an initializer here, we're gonna pass it, that arg
vector and put it on an IVR and then it turned out
we don't really need it. So it's kind of future-proofing because all we're gonna do in our initial implementation here is just call it bored.now
and it puts the description to the command line. All right, so now we can run it locally. You don't have to install
it to actually test it. We're gonna run it here and it says, learn woodworking, alright cool. So step three-
(audience laughing) Is coming, new release,
got to stay on task. So we're gonna update the
version number now from 0.0.1 to 0.0.2 and then we're
gonna run bundle again. That'll update our gem file.lock, and then we've got a change log here. So we got to add, yeah, there we go. Create a new section and
then re-added the board CLI and then I like to do
all this sort of like, versioning ceremony in one nice commit. So they're all tied together
and the stage is clear so I can run rake, release,
give it my MFA code. And now I've pushed up
0.0.2 up to Ruby Gems. So this is awesome. If you go on your computer later today, you can gem install bored
and our gem is there and you can run board and
it'll give you something to do. So we did it! That's all, you have seen everything. I ally-ed no detail. You made a gem, yay!
(audience applauding) All right, so let's talk about gems. In addition to this, if you're interested in
playing around with this and kind of dipping your toe in the water, I created a whole bunch of issues here, like filters on the API or options on the command line interface. And you can read them here. And if you're interested, just open up a pull
request and give it a try, and we're gonna grade on a curve. Like this isn't about excellence. This is about just like
getting some reps in and learning how to contribute to a gem. So if you wanna take
a stab at that, do it, and I'd love to help you
out and we can collaborate. Okay, now for the second part of the talk, like how do we make gems good? If you've ever bought a diamond, you might've heard of a thing called the four C's of gemstone quality. So we're gonna talk
about each of those today with our RubyGems. Their clarity, color, carat, and cup. And so we're gonna start
by talking about clarity. And this is a story for people
who find it hard to focus in a crowded code base like myself. I've got this rails
application called KameSame. It's a Japanese learning tool. I can search for words in it like hap-yo which means presentation,
what we're doing right now. And I can read about that, or I can go and actually
like add it to my reviews and do little quizzes and learn the word. But if I search for a word like Ruby, it's actually not there because initially when I built this thing on a very small dictionary, just 2000 characters and 8,000 words, and I wanted to make that bigger. So I looked online and
there's this KANJIDIC and this Jmdict, these are open source, Japanese-English dictionaries. The thing is that they're in XML. Which means I wanted to
build something in Ruby that would take that XML,
run it through some Ruby and then it'd give me Ruby objects that I could insert into my database. And what it would allow me to do, is go from 2000 characters to 10,000 and from 8,000 words to 200,000. And it seemed like a pretty big win. Now I'm operating in the
context of a monolith and I love rails and I
love working in a monolith because everything's very at hand and I can kind of keep it
all in my head at once. But it has the downside too. If we visualize the monolith, (audience laughing) I've got all the user management
stuff and the quiz module, and like keeping track
of people's progress, I've got the whole like
react JavaScript front end, I've got the search capability. And like I have to mentally
kind of carve out space for this new dictionary import to come in, and it's feels constraining. And I have a bad habit of
making kind of short-term, naive design decisions when I
feel kind of claustrophobic. So I just pulled in
Nokogiri, make a new class. And I take a path, and I
open up that XML document, all into memory all at once. And then I use X path
and I grab the entries and I loop over them and I kind of like cherry pick the parts of each word that I want and then upsert those
all into my item table. And I take a stab at
this and I run it here. And when I ran it, it took
over 12 minutes to run because I was literally paging out. It was just way too much memory. And I knew what the problem
was, but like I'm thinking, and I'm like, I'm overwhelmed by all the complexity around me and I just couldn't really
get in the right head space. And so sometimes, it's nice
to just blow it all away and start in a brand new
and start a brand new thing. So I created a new gem called Ei Wa, Ei, wa, stands for English, Japanese. So it was kind of
dictionary adjacent meeting. And so what it allowed me to do mentally is push all that other stuff aside and just focus on the dictionary stuff. And this allowed me to
think like, hey, actually, what makes the most sense here is to do a streaming Sachs XML parser, which you can do with Nokogiri, but not very many people had done before. And it also gave me plenty of time to get the entity mapping right, and create a good object
model for this to be useful, not just for me, but for others. And then a streaming
public API that was like, gonna be garbage collection friendly. So it didn't chew up so much memory. So I blew away my original
Nokogiri implementation and imported eiwa. And it looks pretty similar. You still say, I'm parsing this file. And I have these entries I'm
just shoveling on these hashes. And then finally,
upserting the items again. And when I run it now, it was way faster. It was up to nine seconds, right? So it was a huge gain. And now when I go in and use KameSame, I can search for things like Ruby and get a whole bunch of results back. And so, I think an
underappreciated benefit of breaking out and starting a new thing. Pure function, none of like
the incidental dependencies onto your big monolithic app. Just go and start a
new folder or a new gem and dedicate some space
for some mental clarity. The next thing I wanna talk about is the color of our gems. And this is a story for anyone
who makes rash decisions, when emotions are running high, like me, and I think of it like
sort of like a pain chart from here to here, where like
at one end of the spectrum, you're not mad at the
other end of the spectrum, you're real mad. But where I do my best work
is like right in the middle. (audience laughing) In care mad mode. And I've got a long
relationship with RuboCop. It's a linter informater for Ruby. And honestly, I didn't
really use it for years 'cause I don't care about
linters and formatters. And I just don't care about
programming language stuff or abstract syntax trees or parsing. It just like, everyone's got a thing. It's just not my thing. But I did see a lot of
teams over the years have pointless debates about like, we should have this rule or that rule. And then like passive-aggressively
changing the rules and unrelated pull requests
back and forth forever. And like, it's just not
a great way to live. And then I saw some teams even, like, where they'd be a tech lead or somebody would sort of like, use it as an authoritarian
means of control to like control how other
people did their work. Like whether it's like really
strict metrics or whatever. And that really, I hate to see it. So I got mad enough at Rubocop that what I wanted Rubocop to be was like an unconfigured
tool, like just take, take all those arguments
and get them out of there and just have everyone be
able to focus on the work, what really matters. And so I very brashly decided like the solution to this was
like I was gonna fork Rubocop. So I literally made a
fork, I called it RubbyCop. In hindsight, it's weird that I changed the rubo part of that name, but I, I went through, I moved it. I did a lot of find and replace and I made a huge, big git diff. I was furiously typing
'cause I was mad at Rubocop. And so like, tempers cooled
and now I'm in care, mad mode. This is where I like to be, right? So I'm like starting to work on this unconfigurable configuration and just picking out all of
what I think are the best rules. But like before I'd even gotten
halfway through it, boom, there's like a major Rubocop release and I look away again
and there's another one and another one and another one. Turns out a lot of people, unlike me, really like linters and formatters
and contributing to them. And it's a super, actively maintained, pretty sophisticated project. And I realized I was
always gonna be outpaced. So since I made RubbyCop, if you look through like these, all of these Robocop releases that I would theoretically
have to pull from upstream and then do all this
renaming and decisions, and there's 106 releases. There was no way I was ever gonna keep up. And so instead of getting to focus on the thing I wanted to do, which was like eliminate stupid arguments. I was over here in this
place where like, oh, I own a linter now, this is great. Like, so, I don't wanna spend
my life maintaining a linter. So I just quit and I stopped. And that's why you
haven't heard of RubbyCop. So it's there, but don't use it. I like to design things, evergreen problems and things to work on that I just find mildly
irritating or antagonistic because it keeps me
motivated, keeps me focused and it's just my happy place productively. And so if I think about like, how do I maximize the time
that I spent in care mad mode, it was again, just like let's
limit these pointless debates and let the architecture follow them. So I created a new gem called Standard. And what standard does is
it truly depends on Rubocop. It uses Rubocop. But, it doesn't use its CLI. Instead it creates its own CLI that just happens to not be configurable. And then that way like, you eliminate all of the
kind of pointless debates and you prevent even
the real authoritarian and controlling stuff from even happening. And if you don't like a particular style, instead of changing the configuration, you just go to standards repo, and you create a new issue and then we can have it out
and talk about it together and decide whether or not
to change it for everybody. Because the idea is instead of
your team having a bike shed and then all of these other
teams having their own bike shed and everyone having the same, nothing fights over and
over again separately. And then you're having to
learn every other teams. Like, Standard's goal is to be one big gigantic community bike shed. And we can just have
it all out in one place and we can have kind of more
portability between projects. And so if you opt into
the Standard lifestyle, as I encourage you to do, life is just a lot easier
and better and fun. But I have also, I'm a
Rubocop customer now. Instead of getting mad at it, like I've really come to respect the amount of work that Bozidar and Koichi and all these people do. It's a super sophisticated project. And I like really, I have
an appreciation for him now. So all that to say like, don't let your priors,
color your thinking, like your biases. Like you might wanna fork a thing or build a competitor to a thing, but like, there might be a
way to be more interoperable or even contribute back to the
thing that you're mad about. All right, so the next step
let's talk about carrot or the size of the weight of our gems. This is a story for people who find it hard to say no, like me. So back in 2011, I was like
getting mad at my computer. I was like, why it's so hard
to just JavaScript and rails. And me and Corey Flanagan,
we sat in a coffee shop. We started this gem called Jasmine-rails. And it's like, what I
call like a glue gem. So it's just like on a connects
these two things together. What Jasmine did, if you're not familiar, it's a spec like syntax for writing tests. So you can write a test like this and it had a little HTML
runner, it looks like that. And so Jasmine was just providing just that test DSL and JavaScript, and just a little tiny reporter API, tell you how many tests
were passing and failing. And then the rail side,
what we were relying on here was rails 3.1 had just released, which was the first time, thanks to the asset pipeline and Sprockets that you could have like an engine include a bunch of like JavaScript and CSS assets and stuff. And so we were like, this is great. This is gonna enable us to build this gem. Now, if you dislike the asset pipeline and Sprockets now, you should have tried
it written 3.1 came out. Lots of performance regressions, it came in really hot,
lots of breaking changes. It was a very challenging time. So this thing depended
on Sprockets from rails and rake and ERB. And then like the work in the middle was the glue code, right? 'Cause the glue gem. And so what we did here was we had to have a custom rails engine that could host that runner. We had a Phantom JS installer, so you could run it from the command line in a fake environment. And then a whole CLI, because Jasmine didn't have a CLI for like formatting all
these results appropriately for your CI system. And it just did too many jobs. All of them poorly, it was pretty slapdash. And whenever that happens, because like, it just has
a lot of responsibilities, you're gonna get a lot of pull requests. So we got a lot of pull requests and I kind of just like merged them all 'cause I assumed like surely
other people will help me maintain this if they
contributed features. And that turned out to
be a false assumption. And also I just didn't have a lot of good tests around everything. And so I didn't realize
that I was kind of just like bringing it, hoovering in
like a lot of complexity, but I just like said,
yeah, let's pull it in. Like make it the best we can do. I've never had a really
popular gem before. But what I learned is you
gotta be really careful. There's configuration where you're like, change where a path comes from and then there's configuration, like it changes the mode
that you're operating in, like turn on and off features. And so there's like a lot of
different optional features in this gem now. And maybe in my app, I
only use one of them. And maybe in the thing that runs on CI, it actually uses none of them. But like if somebody is angry
about a particular issue and they open up like a new bug request, like maybe they got these
three flags turned on and they're interacting in some way that I didn't anticipate
and I didn't test for, because like it's a competent,
horrible math problem. It's like if you've got
seven optional modes and they can all interact with each other, that's two to the seventh power or like 128 different
configurations of your gem, you got to test all of those
if you want it to work. And so anytime we got one
more pull request, marginally, it might look like, oh, this is just one more
little true false flag. But in fact, like if
it's the eighth thing, now we're two to the eighth and now we have 256 modes. So you gotta be really, really careful about configurability like this. New options not only
make it more confusing and harder to maintain, but harder to test and be assured about. Like every time I changed
anything in this gem for years, I felt like, oh, it was probably
gonna break somebody else. And I eventually, I just grew to resent it
and I stopped working on it. So we had 67 people contribute to the gem, but at the end of the day, I was the only one
accountable if it broke. And because I'd pulled in so
much of other people's code that I didn't read, like no one understood how it all worked. And so last week I just merged
to the final PR to this gem to say that it's no longer maintained. And so now, all that's changed here is no one's accountable if it breaks. So don't use that gem. So nine years later I had a new Mac book, but the same problem. Why is it so hard to
test JavaScript on rails? I've learned some lessons this time and I create a new gem
called Cypress rails. This is also a glue gem
and it connects Cypress, which is like, a web testing
tool and JavaScript with rails. And it's got a test
API, it's got a runner, it's got a great CLI. It brings more to the table. And on the rail side, I just use kind of more
hardened to pieces, rake, rail ties, action pack. And the glue's responsibility
here is much less. Here I'm just responsible for like, kicking off a new test server, sort of like your system tests do. I roll back transactions between tests. So there's like a little
like route that you can hit to say like, hey, I wanna isolate the
data between these tests that you can do. And then there was a
superclass that we wrote for mini test, where you could inherit from that and then like run rails test and it would include your Cypress tests along with your system
test cases and stuff. But it turned out that in practice that was really slow and cloogy 'cause Cypress didn't know about it. It didn't work with all the extensions and I could just tell it
was not gonna be something I would use. So I had a tough choice to make. I had to choose between, remove the feature and
potentially make somebody mad or continue supporting
that future in perpetuity for fear, even though it's not very good, mostly out of fear that
somebody might get mad if I removed it. But the Jasmine rails
experience had taught me, like the right thing to do
is just to get rid of it. Now, if you publish a gem and it doesn't have a particular option, no one's gonna know to thank you that your gem doesn't do
this unnecessary thing, but like you need to
have your own backbone and kind of stand up and
be a bit of a Vanguard for complexity creep. So now there's only two modes, right? In that gem and it makes
testing really easy. I've got a test out
for transactional mode. I've got a test that for
non-transactional mode. And so I can be really assured that like whenever I release something, as long as the test passes,
it's probably gonna be okay. So yeah, the lesson there is just, don't let complexity, whether
it's complexity of like, you feeling like you got to do everything or other people telling you,
you got to do everything, a bunch of pull requests
like way down your gem. 'Cause like the last thing you
want is five years from now hating the thing that you created. So last but not least, we're gonna talk about
the cut of your gem. And this is a story for anyone who doesn't take feedback
particularly well, like me. So Test Double our company. It's an illusion to the name Test Double, which is like it to be a
stunt double in your tests, any fake thing that
stands in for a real thing when you're testing. Now most mocking libraries, a mocking library creates Test Doubles. It's just one of the same effectively. Most of mocking libraries, they like their API is
kind of make it real. Like they're focused on like
how do I take real things and sort of poke holes in
them like Swiss cheese. And what they really do
is they facilitate testing of hard to test code is essentially how most
people use mocking libraries. But my preferred kind of mocking library, same kind of tool set, but
like totally different framing. It's like paint by numbers. Like how do I help facilitate
writing easy to use code that just happens to be via tests. So there's a way different mental model. And the way that I kind
of got into this mode was in the late aughts from a
great Java jar called Mockito. And I kind of poured it, in 20 times, a spiritual successor for
Ruby with a gem called gimme. And so I made, in my spare time because I had a job that
was kind of constraining, a little bit stuffy at the time. And so I was using gimme to have fun. So I got to play with like a
lot of Ruby meta-programming, that was fun. Instead of doing unit tests, I did executable
requirements with cucumber. And so like all of the
documentation was executable. I thought that was cool. A little some clever inheritance going on, but mostly I was just
like having a good time, but I was really proud of the API. I knew that the API was good. I knew it was an improvement over anything else available in Ruby. I still use it. And Jim Weirich who was
a Ruby hero of mine. Unfortunately he's no longer
with us, but he wrote rake. He was an early contributor to Ruby gems. Great person, he also had
a gem called Flex Mock, which was a mocking library. I saw him at a conference
and I pitched Gimme to him. I was like, check this out. This is really great. And I was really nervous
and I waited and he said, it's interesting! He was like very positive. He was also a really positive guy, but I was super heartened, I
was really excited by that. And then later on that year, he sent me a pull request to Gimme. So he was like using Gimme and I was like, oh my
God, my life is over. This is the best. Like my hero is using my thing. And it was just really exciting. So then I saw this
tweet when he said, hey, he was like dismayed
by the use of Cucumber instead of Unit Tests and stuff. And I was like, that sounds
like something I did. And then I looked at the date
and then I looked at this date and I was like, oh God, my
hero just subbed tweeted me. (audience laughing) And it was really demoralizing. And the worst part of course is that Jim was absolutely right. 'Cause I would start to
get issues like, hey, here's a bug from JSON. And I try to like pull back
the covers and work on it and realize that all I had
with these integration tests, I didn't have unit tests that were actually forcing me
to write well factored code. And it was just a big yarn ball. It was changing one thing here would break something over here. So it was like the right idea. It was the right API, but it was just the wrong execution. I didn't do my best work
to implement it well and to be maintainable. So I just gave up for 10 years. That's one solution to the problem until today, because breaking news, I got a new gem to announce,
it's called Mocktail. And it's something I built over the summer because like, I've decided
to gems can have sequels. So it's a totally fresh start. It's the same kind of API, but this one, like I just
decided to code it good instead. You can check it out online. We're not gonna go into
the API and stuff today, but it's called Mocktail. Hopefully it's well documented. We're gonna release some
screencasts and stuff over the next month and a half, but same kind of deal. I've got a day job, which
is more flexible now. And I spent time thinking hard about the API over years, really. Building prototypes
and throwing them away. I strive to get a hundred
percent code coverage. Had to be sure it all worked. Lots of small well-named classes. So I could be sure that
I'd be able to maintain it. And I was feeling really good about it. And then I had a lot of
thread safety issues. So then I fixed those, but
I was a little less happy because I don't like dealing
with multi-threading stuff, but overall, like big picture, I had been proud to make gimme, but I was super proud to share Mocktail 'cause I really believe in this and I think it's gonna
work over the long-term. And more than just pride, like I know future me is gonna thank me because it's gonna be more maintainable and because I've got a
really great test suite, if like Ruby changes under me or I needed a dependencies break, I'll have a better chance
of fixing those quickly. It's also more inclusive. If somebody wants to
work on this gem later, like they'll just be able to clone it and then run bundle and run rake and really quickly like
get up and running. It's not gonna be arcane. And it's exemplary in the sense of like, it's a good example of my work. I'm pushing up my best work to GitHub so I can share like, this is how I like to code, right? And not a lot of us have
a lot of portfolio work. So if you've taken taken
the time to write a gem, it might be a good time
to kind of show off what you know and how
you like to write code. So over the years, even though like, I don't wanna discourage anyone from like just playing around and
like programming for fun, if you do plan on like
maintaining something for a few years, I've never regretted taking the time to measure twice and cut once. So that was a little
bit about the four Cs. And I hope that you take this talk and take some energy today, and think about like maybe there's a gem that you could create or contribute to. And if you do create something, I would love to hear from you or even if you're having
trouble or issues, reach out to me by email or DM me or tweet or if you've got any feedback on the talk or you'd like to share
it with somebody else, we'll be sharing a video real soon. And I'd love to hear from you, but last but not least
before I close and go away. I just wanna share real quick. This is a very special RubyConf because this is actually the
10 year anniversary this year of my very first Ruby conference. I spoke at Rocky Mountain Ruby in 2011, just down the street in Boulder and a guy named Marty
Haught was organizing and he took a chance on me. And he's also a director
at Ruby Central today and organizing today and I got to speak
alongside heroes like Jim. And it really changed my life. This is actually after Jim sub tweeted me, but I took it in stride and
it was just a great experience and my life has been transformed. That's actually me. I oddly enough, that
talk was about Jasmine and the Jasmine rails gem
I just told you not to use. Time marches on. We all learn lessons. And that's not all, it's also the ten-year
anniversary actually next week of the founding of our
company, Test Double. And that's really exciting. I can't believe it's been 10 years. (audience applauding) Hell of a run. So we just started as two people working at like mostly
medium and small companies, but now we're almost
a hundred consultants. A lot of you in the room have worked with or at, or are working at Test
Double or with Test Double and it makes me so happy that we get to work with
so many cool people. And we're at clients now, like, GitHub and Zendesk
and Betterment and Gusto, just working with some of my
favorite companies and products and just really awesome teams. And it's been a wild
ride, I'm super grateful. If share our mission of
like wanting to like improve how the world writes software, you can learn more about
like being a consultant at careers.testdouble.com. And if you're like, at work and you're doing something ambitious, or you could use some additional help or you're maybe like
got some legacy rescue or like wanting to learn some new skills. Our service is primarily
to just join your team and help you out and get
some done alongside ya. And you can learn
more@services.testable.com or just send that link to your boss. And last but not least like really, I just wanna slow down for a second pause and really say, thank you because like my life has
transformed over the last 10 years. And it's all thanks to the Ruby community. Like Test Double would not be
the company that it is today without all of you and being able to engage and meet people. Some of like my favorite, like lifelong relationships and friends I've met in this community. And the fact that I
still get to go up here and yammer on at you is just, I'm tremendously happy to be here today. I'm really, really grateful. And I wanna thank you all
personally for your time today. So thanks. (audience applauding)