(energetic music) - Okay, so the title of this presentation is The Selfish Programmer
and it is an exploration of what you can learn by writing software for yourself, as opposed to on a team or part of a larger organization. Now, my research shows that
the three most important traits in the successful solo programmer are that they be antisocial,
egotistical, and irresponsible. And we're gonna spend time
today discussing all three. Now, you may know me by this old picture that doesn't quite look
like me or as @searls on Twitter and GitHub and I
am a self-professed expert in selfish programming. I come from a company, Test Double, we're a consulting
company, our double agents join client teams as additional developers to work alongside them
and get things done, while also searching for ways to help the whole team improve over time. Now, I'm here today
'cause I have a problem and that is that learning
Japanese is really hard. I've been working on it for 15 years and I still have a long way to go. But a couple years ago,
I found an application called WaniKani and it
helps you memorize kanji and vocabulary, it uses a
spaced repetition system or SRS to time when
you should review items to help you memorize them. So it'll challenge you to
remember something for a day and see if you can
remember it for three days and if you can, then maybe a week, but if you get one wrong,
you'll just review it more soon and as time goes on it might be a month before you see an item. And then the app assumes that if you can remember something for six
months, then you probably know it, and it'll consider that one done and you can focus on the
remaining 8,000 words. The interface, of course, is
like a little flash card game. So you see Japanese and
then you provide a reading. So, (speaks in foreign
language) in this case. As well as it'll challenge you to provide the English meaning and
this word means tug of war. And because I got it right,
the timer system's gonna push it off to a later review date. So what I found was that
this application was awesome for teaching me how to recognize Japanese and understand it in English. It was very good for that,
I can read a lot better now than I used to, but for
being able to produce Japanese words out of English ideas, it just wasn't very
effective, because it doesn't practice that muscle. So when I was talking to
my conversation partner, I'd often be tongue-tied trying to think of the right word, so
I built an application called KameSame, which is a companion app to WaniKani and is
literally the same thing, except in reverse. So you see an English prompt and then you use a Japanese keyboard
to provide the word and if you get it right or wrong it uses the same kinda timer
system for helping you memorize how to produce the word. Additionally, it was important to me to make it a progressive web application, so that it could survive disconnects, as well as it's a great way to practice the kana flick keyboard
that's popular in Japan. And most importantly, 'cause
this is all about learning, building KameSame taught me a
lot about selfish programming, and so today, I'd like to start talking about why it can be good to
be a little bit antisocial. So, world one, stage one,
the selfish programmer is unambitious and because
it's easier to stay motivated when your goals are
incremental and achievable. When goals are too
ambitious, we run the risk of exhausting ourselves
without accomplishing anything, which might lead to us
quitting and giving up. 'Cause work, work is different. At work, our apps are big, they have lots of different parts, they
have their own operations, and those have their own parts. And we can trust that
if somebody is working on this part of the app
over here, we can safely focus in this area over here, knowing that they've got our backs covered. But the problem with large
apps is that our brains aren't big enough to fit them all. If we try to quickly
move from area to area, the amount of context which it can cost is sort of like memory
paging and it's just wasteful and inefficient. So people tend, over time, to specialize in large systems on one area,
even if they run the risk of kinda forgetting how
to build an entire app all by themselves. And just talking about this
problem makes me nostalgic for 2005, when I first
started using Ruby and Rails, 'cause on my mind, what made Ruby famous was that it enabled
developers as individuals to make small, useful
apps without any help. And because Ruby makes it
easier to keep the whole app in your head at once,
it's also great for moving more quickly throughout a code base. So all that nostalgia
was making me excited to create a new, solo Ruby
app for the first time in a few years, but
even after several days of effort, I unfortunately only completed one small part of this
flashcard app that I wanted to build and it was useless by itself. And it got me thinking
about all the other pieces that I'd have to build and
it was really demotivating. Somehow, over 14 years,
I'd un-learned how to make small things, I'd become too ambitious. And so I instituted what I
call the one weekend rule, where, if I'm not able
to release a project by Sunday night, I don't do it. And the one weekend rule
is a liberating constraint, 'cause it's forced me
to shrink down my dreams to something small enough
that I can get done, with the side effect of also
fitting inside my brain, so I'm able to work more quickly as I do. So I tried to imagine
the tiniest useful thing I could make and I decided
on a gem to tell me whether my flashcards were
ready for review or not in WaniKani. It's called $whenkani and when you run it, it'll tell you, "Oh, you
know, maybe you don't have "flashcards now, come back in 11 hours." And when I do have flashcards,
it'll print the URL that I should go to to go study. Really simple code, of
course, it just uses net/http and json and implements a single method, says, like, "Hey, how many
seconds to your next review?" So it goes out to a URL,
parses the response, if there's no error then
it reads the information. If I've got reviews available, of course, the answer is zero seconds, but otherwise, it'll do some math. And this was super simple,
it was easy to test and it was easy to fit in my head. And importantly, it gave
me experience working with WaniKani's API, so this helped solve one part of the larger
application that I wanted to build and would make it easier
for me to build that later. So, excited, for my next weekend project, I decided I wanted to
take a bite of the apple and build the English to
Japanese flashcard game. But that was just too much to do at once, 'cause it involved complex game logic as well as a complicated user interface. I'd never finish that in one weekend. So I carved off just a small
piece, just the game logic, and now to solve the problem
of how to make that useful so I could iterate, I decided to wrap it in a throwaway command line app. So that app looked like this
and I spent several weeks just working on this, I'd
get an answer right and wrong and change what got printed out. Realize that there were a lot of synonyms, and so I had to handle that gracefully. I did things like persist
my progress over time and set up all those timers. And because I knew this
was a throwaway CLI, it was intentionally trivial, it was just a while loop with read line and a bunch of put statements and when you strip away
those bits, what you're left with is actually interesting. I had code that actually
could create real review cues that could accurately judge the responses that I was giving it and that
could persist my progress, so that weekend project was
actually a huge success, because it became the
actual code that still runs the real web application today. So incremental accomplishment
can be really motivating and that's why the selfish
programmer is unambitious and works to shrink their dreams down until they're easy to accomplish. All right. Let's talk antisocial stage two. The selfish programmer is
ungrateful of Open Source, and understands that if
dependencies are added carelessly, they may create an unmaintainable
mess for themselves. So I like to envision
applications like pyramids. At the top is our actual application code and below are all of our
dependencies and at work, we're used to really large applications and so the marginal cost of adding just one more gem seems pretty low. But when you're solo you know
that your time is limited. And so if we add too many
gems, things like upgrades and workarounds may
eventually consume more time than we have to give the app. But arbitrarily limiting
ourselves to just two or three gems is not much
of a solution, right, because then we wouldn't be able to build very useful things. And so this is why I started to categorize my dependencies based
on how much I trust them and their maintainers to
take care of stuff for me. For example, even though
Rails is very large and installs 41 other
gems, I trust Rails core to carefully curate those
things and make it easy for me to stay up to date. And so, I conceive of my jump
file as sort of two halves, there's smaller gems
maybe where I don't know the maintainer or they're
not very well-maintained and if something goes
wrong, I'm probably going to be on the hook for taking care of it. And then there's the
really popular gems where certainly my little tiny app isn't going to be the first one to discover a problem and somebody else is probably
going to write a patch for me and I worry less about those ones. Separately, at work, I've been conditioned to never reinvent the
wheel, that is, write my own code if there's already other code that could do it for me. But when you're solo, you
can choose to be ungrateful, and write some code, even
if there's a gem that claims to do the same thing. So, for example, maybe a
gem works, but it fetches the whole universe just for one feature, and the gem's usefulness isn't
worth the maintenance cost of 28 additional gems. Another problem is when a
gem is too hard to learn, getting frustrated by a
framework or a library is one of the top reasons that people
quit their solo projects. And, you know, reinventing
the wheel might be bad, but outright quitting is certainly worse. So, for example, talking
about specialization, at work, I've never
really been responsible for authentication features,
but for my solo app I was by myself and somebody
had to figure it out. And in the past I've tried to
use the popular gem, Devise, but I've failed miserably
each time at understanding it, and so I was afraid that
if I tried it again, I'd get frustrated and I'd
quit the whole project. So I started with a super
basic password field, installed bcrypt, and I used
Rails' has_secure_password, and it actually worked,
that gentle climb was all I needed for the first five months of the app's existence. And yes, I eventually
added custom features like changing passwords and
verifying emails and, you know, a forgot password reset link. And yes, those customer
features were probably complex enough that I would have spent,
you know, more time writing them than just learning
Devise in the first place. And yes, I'll admit that
generating custom tokens and saving them and emailing
them and then handling the little click and
the verification emails, it all felt silly because
I knew that Devise could do this for me, but
I'm still proud that I did it by myself, because I understand my implementation completely. And KameSame gave me a
safe place to practice something that I wasn't
very comfortable with. So now I don't feel so
stupid when I'm talking about authentication anymore. So you can try to solve
every single problem that you face by Googling
for gems to do it for you. But just because dependencies
are easy to install doesn't mean that they're going to be easy to deal with later. So this is why the selfish
programmer is ungrateful. You know, whether at
work or solo, it pays off to think critically about
the trade offs that we face for each dependency that we add. All right, antisocial stage three. The selfish programmer is
ungenerous and doesn't worry too much about making code reusable. See, at work I was trained
to share as much code as possible in order
to be a good teammate, and so whenever I added some
code I could, of course, could have put it to live with the feature that I was writing, but more
often I put it, you know, in a place where others would
find it, like in a model and then I invoked it for my feature. And this is fine, but eventually
when teams work this way, it can really slow them down, for example, maybe a second feature also
adds some code to that model and calls it, a third feature
maybe reuses a bit of code and a fourth feature reuses some more. And after this if one of
these other callers needs to make a change to that shared code, you have to consider all the
other things that call it, because it might blow them up, they might break their behavior. And so it requires a degree
of caution where you have to check every single call site
for any bit of shared code. Additionally, when you
have these kind of like high-traffic, high-churn areas like models and Rails applications, it
run the risk of drying up internal private methods and
where they call each other and pretty quickly you
can end up in this tangle where everything calls everything. And so making any change
can be really precarious and difficult. So instead I prefer to soundproof my code, and that means I put it in,
instead of shared places I try to have the code
live with the feature and I only extract it if it
proves to be valuable later. So as a result, most of
my code is pretty isolated and I can safely and aggressively
change and refactor it, and most of my models and
things are just dumb value types that I pass in and out of those features. This impacts how I divide
responsibilities when I'm coding, for example, this controller
action does two things. First, it invokes feature
code to perform a search and second, it invokes
general utility code that formats the results for the API. I split these responsibilities
up to soundproof the feature code and minimize
the general utility code. So the feature specific code
is only called one time, which means I can change
it really aggressively, and we can be as messy as I needed to be. But the general purpose
code is kept really minimal, because it's called in
seven different places and so it's much more work
if I want to change it. Separately I don't allow
word, the word model or any active record model, to have much, if any feature logic in itself. I treat Rail subclasses as a DSL for configuring rails and not a place to put my own custom code. Because if I were to add just one method to my word.rb file, it would look small, but it would actually be its 312th method. And that means the contract
between it and the people who call it is kind of murky. But if I create a separate
class with just one purpose and one method, it's going
to have a clear contract with anyone who calls it,
and I've got evidence, soundproofing code really works, because for over one year KameSame started as actually, this code base
was just an example sentence search engine that was
completely unrelated called SenTensei, and only
later did I add KameSame right on top of the
same models and database and it required zero changes to SenTensei for them both to work, and
best of all, a year later I deleted SenTensei and KameSame was none the wiser,
everything just worked, the code was separated. So at work code is often
treated, especially by management as a valuable asset, and
that gives us this mindset of thinking about like reuse
will somehow make our teams go faster, but the more
places that call a function, the more careful our changes have to be and that's why the selfish
programmer is ungenerous and not afraid to write
a little extra code in order to gain the
flexibility of changing it aggressively later. So that's a little bit
about why it can be nice to be antisocial when you're
programming by yourself. So now let's talk about the
virtues of being egotistical. So world two, stage one. The selfish programmer is delusional, because sometimes it's
necessary to believe that your code is good enough to ship, even when it's really bad. (audience laughing) So this is the KameSame homepage, you'll see that you have
progress here like XP and level and that sort of thing. And it's driven by this simple little API. It's a small response, but
look at how many queries it does, it's kind of
convoluted and, this at work, this code never would
have passed a code review, because once merged the team
collectively owns that code. So the, you know, any
problem becomes the team's whole problem and this
results in our teams generally having a higher bar for quality up front. But if you never create a new branch, then you don't need to worry
about pull_request reviews. (audience laughing) So...
(audience laughing) because this is a solo project, I'm just pushing to master all the time and I just try to keep it working. So, the selfish programmer is delusional, because they choose to believe
that features are ready, as soon as they work. And if we try to perfect
that code in advance, we'd only be guessing as
to what the ideal design and optimization should be. Instead we push this
messy but working code, we can take time and observe
errors and bug reports and feedback and gradually improve that code's behavior as we learn. And so over time I found
myself adding to this route and subtracting things and adding code, but all the time resisting the temptation to optimize its performance. Instead, I waited some more. And after a long time
passed without changes I could be confident that
the feature's behavior was always mostly correct, 'cause code never tells us when it's done. So I just choose to believe that once code no longer needs to be changed,
then it must be right. And that means then we
can turn our attention to how we might optimize it. So recall, this is a lot of queries. And I needed some way
to gather all that data without making so many
trips to the database, because if you were to think
about this architecturally, we have a lot of Ruby logic
up top and that's great for prototyping, it's really
easy to change, but it's super slow, right, because calling
to Postgres so many times. So I spent a few hours and I reimplemented the entire feature as four SQL
views, and Postgres is great at doing math and aggregation
so these SQL views do everything that the Ruby
was doing, except faster. So, because that behavior is finished, it's okay that this is kind of uglier and harder to change. And fun fact, actually,
Active Record can talk to SQL views as if they were models just overwrite read_only
to true to be safe, and you can query it just like you would any other model in your system. So, now for the payoff,
we're able to go from all of this code here, to just
a single actor record model that was backed by a SQL view. So the Ruby is now
responsible for much less, and the code is also much simpler. And you can even look at the architecture, it looks simpler right,
just a little bit of Ruby calling to an admittedly kind
of complex Postgres query. And predictably that
route is now much faster than it was before. And so to recap, I
recommend you know we start by shipping code as soon
as we can make it work. Let those changes simmer
until we've made it right. And only then make it fast
by pushing that code down to, you know, a faster layer. So peer pressure to
write good code at work has kind of made me a
perfectionist over the years, but this results in the
future-proofing behavior and optimization, before
I have any real data to base those decisions on. So that's why the selfish
programmer is a little bit delusional, believing that
messy code is good enough to put in front of users,
so that they don't waste time guessing how to perfect it. Next up, egotistical stage
two, the selfish programmer is narcissistic because they ask only how an activity will benefit
them before they decide whether to do it. So, at work our large
apps usually have lots of units and lots of tests
of those units that call them and then assert a response. We also have full stack
tests that interact with the whole system
and then verify it works, and on a team, these
different types of tests, address very different needs,
full stack tests, for example, help build trust that our app is doing what it's supposed to do. And when we have lots
of things, tests create a safety net so we can focus on one area with being sure that we're
not breaking other things. And third, tests can help
express our intentions to our teammates and to our future selves, of what we're really trying
to accomplish with our code. But these testing benefits have less value on solo projects, like
you're the one determining the direction so you don't need to prove to anybody what you're doing. The app is probably small
enough that you can focus and move quickly. And when you're by
yourself there's probably more efficient ways to
denote your intentions to yourself than writing
a whole bunch of tests. So because of this I took
a new approach to testing for KameSame, it's called AI testing, and I'd like to demo it for you now. So here's a sped-up
recording of a full test run, single test is going
to wind through a bunch of different parts of the application. You've seen browser testing before, it looks a lot like that, doing things like doing some flashcards, searching. Going to the account
settings, change your name, change your password, logout,
login, that kind of stuff, and this is just one
test, but it actually gets pretty good code coverage,
it's like 80% coverage, so I'm relatively confident
that when this test passes, the app probably works, and I
go ahead and push to master. The downside though is that
this test three whole minutes of my build to run. And I want to be able to push
the master and go as fast as I can so that I can go about my day as quickly as possible. So this is where AI testing really shines. First, it looks at only the
most recent changes to the code. And then it only runs the unit tests that were affected by that change set. And then finally, the browser
based test dynamically adjusts to only cover the affected area. So here's the same test run again, after I make a change just to one feature. It's gonna do some flashcards,
but then it's going to hone in on the custom spelling feature, it's going to spend extra
time there, go back, perform a search, verify that it's there, and so on and so forth. And of course, the
coverage is much much lower for this test run, but the
confidence is still high, because the test focused on where the code had actually changed. And best of all, I got the
build down to just 20 seconds. And so I should confess,
however, that AI testing stands for average intelligence. And this was just me recording videos with simple code enabled. Truth be told, I actually didn't write any automated test for KameSame, (audience laughing) 'cause I wouldn't have
gotten a good return on my investment. Remember, the primary remaining benefit was it telling me whether the app worked, but I intentionally designed
the app to be really easy to manually check whether it was working by being focused and narrow. Because on teams good testing tends to pay for itself right away,
you know, yes, you invest in writing them and running
them and changing them. But you know, you get to run them locally, you run them in CI, maybe
your teammate makes changes and then runs them in CI and by the time that you've merged them into master, you have probably gotten a
payoff, you know, more benefit than you've invested in good tests. But when you're solo, that
investment cost is similar, you still have to build and
run and change them all. But the value is mostly limited
to running them locally. And then once MCI and yeah,
they might pay off eventually. But at least initially, there's
a little bit of a gap there. You see, most activities
the developers do to deal with software problems can be categorized as either trying to maximize
MTTF, or mean time to failure, that stuff that we do to
prevent things from breaking, or minimize MTTR, the mean time to repair, the amount of time before we fix something when it does break. It's probably important
for us as professionals to balance our skills
between these two types of remediation. So personally, I've spent most
of my career mostly focused on maximizing MTTF, I rely
on tests and compilers and linters to avoid
production issues outright. But by not writing tests,
KameSame has given me a great chance to improve my
skills at monitoring servers and analyzing error reports
and debugging problems. So a lot of people assume
that things like testing are always worthwhile just
because somebody told them to. But when you're solo, it's your
time that you're investing. So you need to be more
critical like, this is why the selfish programmer is narcissistic. It was only after deciding
that I was spending my time to benefit myself that I could do the math and then stop feeling guilty for the fact that I wasn't writing tests
for my own solo projects. Egotistical stage three. The selfish programmer is domineering, because they're comfortable
saying no to people a lot. Most people agree that
great software requires a clear, narrow vision. But telling people no is really hard. When you're solo, you're
not just the only developer and the only tester, you're
the only product owner, too, and product owner is a really hard job. At work, product owners
have to juggle the concerns of many different stakeholders,
marketing, finance, security, support, not
to mention the needs of the application itself. And product owner has to
set priority which is going to make some people happy
and frustrate others. So I decided early on, I was always going to release KameSame for free. 'Cause as of today, we
have about 2,300 users, who've done millions of reviews. But as of today, KameSame still
only has one customer, me. And I have zero other stakeholders. So that makes it much easier
for me to tell people no, as product owner, I knew
exactly the features that I wanted at first. But after launch, I kind
of ran out of good ideas. I just needed some inspiration. And so I started a thread
at WaniKani's forum. And it's got a whole bunch of posts. Basically, this is me
just farming ideas out of this community of how I
can improve the application. So I give KameSame away for free, and I get free ideas back in return. And of course, if you read
that thread, you'll see me saying no to people a lot. Because if a feature doesn't appeal to me as KameSame's only customer, I don't want to spend my personal time building it. But sometimes I get a
message like this one. It was from a colorblind person who said that the color coding of
kanji and vocabulary was too subtle and resulted
in accidental mistakes. But I'm not colorblind. So I wasn't sure that fixing
this would benefit me somehow. So I looked closer at it. And for context, here's a vocabulary card, it's asking for a
multi-character adjective. And here's a kanji card asking for a single character response. Now if you desaturate them and
then overlay you can see how just visually similar
these two things are. So I just looked closer
and realized I'm already doing a lot of client-side
validation here. I'm checking for empty answers
and alphanumeric answers, phonetic answers to kanji questions. I realized I could fix this by just adding one more condition. So, checking to make sure
that the length is one if I'm asking for a single character and then updating this message. So now the app catches
these kinds of mistakes. So if I type in that
adjective, it will correct me and I can backspace and enter
without the erroneous failure being recorded. So even though that feature's inspiration was somebody else's problem,
the improved validation has helped me many
times too, 'cause I tend to move too quickly through the app. In fact, most of my favorite features of the app were somebody
else's idea solving somebody else's problem,
and I benefited from it too. And so you know, at work, it
can be easy to just shut up and blindly follow orders. But the selfish programmer
gets lots of practice being domineering, because they
must control their own priority and direction. So yeah, at work it can be
uncomfortable to challenge a requirement that might
be handed down to you. But I found that teams that
struggle over direction together tend to write better software. So, it's not enough to be
antisocial and egotistical, the selfish programmer
must be irresponsible too. And I mean that literally,
like I don't want to be responsible for anything. So, world three, stage one,
the selfish programmer is untethered from operational
responsibilities. Their apps should run
themselves so that they can go and focus on they want to be doing. Now most apps, they end up
with lots of operational work that has little to do with
the application itself, stuff like security, performance,
upgrades, and monitoring. And originally the term DevOps
meant developers responsible for their own apps' operations. And the idea was, well, like
we saw with automated testing, if developers own
operations, they'll find ways to simplify and automate
it, so that they can go back to focusing on development,
but it hasn't really worked out that way. Instead, DevOps has been co-opted by all the large cloud service providers and they market increasingly
complex customizable services. And this is created so much
new work that now companies are hiring full time DevOps people just like they would sysadmins. And so because the definition
of DevOps has changed, I'm now a no-ops advocate, I
want to focus on programming, so my goal is zero
routine operations work. So to reduce my apps operational
costs, my first step was to minimize dependencies,
follow conventions, try to reduce the number
of runtime servers that I would need, but honestly, this only helped a little bit. What I really wanted was for
somebody just to take care of ops for me, I wanted to
be able to walk into a store, and buy some ops off the shelf. (audience laughing) Fortunately, that store
exists and it's called Heroku, not, not, yeah, Heroku.
(audience cheering) Not only is Heroku still
good, it's better than ever, and by helping me be
irresponsible of operations work, Heroku really captures the
original spirit of DevOps. So this tiny file is all
I need to tell it how to run my app server and that
I want to run my migrations on every push. And I only pay $7 to keep the app up and $9 for all these automatic add-ons, like Heroku Postgres and Bugsnag and SendGrid, but easily my favorite Heroku features are deploy pipelines and review apps. You can see 'em at work here. So I'm going to go into
Git and change a background from white to purple,
commit it to a branch, I do all my coding in
the GitHub web interface. (audience laughing) And as soon as I open that pull_request, Heroku's provisioning
a one-off application against that PR branch, I'll click in and I'll
see my purple background and this is such an awesome way to work. You know you don't have
a staging environment or anything like that
anymore, you just get to see somebody's PR
in action, play with it and color your feedback,
it's really awesome. And just set up review
apps, you just create a simple little app.json file, you tell it what build pack you're
using, what add-ons you need, and any script that you
need to run after the fact. And my script is simple. It's just run my migrations,
download some sample data, and then create a user
so I can go in and test. And that's it, that's really awesome. I've seen companies
invest millions of dollars into DevOps and fail to match
the developer experience of a $7 Heroku Dyno, it's really cool. But most companies are
going to resist no-ops and platforms like Heroku,
because it's really hard to admit that they're not
special, unique snowflakes. And it's true that no
two apps are the same. But you know, Rails taught
us that apps actually share a lot of common problems. And that's true of operations too, things like hosting, deployment, security, so why not outsource that to somebody who specializes in it? So that's why the selfish
programmer is untethered by operations. They know that they're
going to go further faster by focusing on what
they really want to do, write software. All right, irresponsible stage two. The selfish programmer is fickle. And because solo work
is a creative outlet, their opinions are going
to frequently change. So at work, everyone
brings their perspective and their coding style, and on some teams, everyone just sort of
codes their favorite way and that might make people happy at first, but inconsistency can lead to
confusion and conflict later, which would slow the team down. Ultimately, some things,
even things we care a lot about just don't matter that much. And code formatting is one
example, if the Ruby interpreter doesn't care, why should we? So that's why some teams try
to normalize on just one style for their code, they start by
choosing a way to do things and everyone does their
best to conform to it. And then they hope that the code is going to be more consistent and
hopefully make it easier for them to own and maintain long term. But one of the joys of solo
coding is that your way is the only way, you're finally free to code however you want. But I've found that when I'm
solo I'm also really fickle. My favorite way to code
changes all the time. And so whenever my style
changes, my code becomes inconsistent and over time
and across repositories, it makes it really hard for me
to maintain my own projects. So that's why linters
and formatters exist, like Rubocop, that enforce consistency. But the problem is that,
for each individual project, you know, we'll eventually
create a large, custom style configuration and time
spent arguing coming over your linter config is
just wasteful bike-shedding. And since every project is
likely to have a different configuration, you're just
gonna waste precious brainpower keeping multiple styles in your head once. So that's why at Test Double, we created the gem Standard, it wraps Rubocop with a single configuration
that is locked, and you're not able to change it, and so it can't be customized. It's made the CLI real simple. You just run Standard to see your failures and Standard Fix to fix them, so trust me, this is not Justin Searles style Standard is actually the
result of many compromises. This is how I actually
prefer to write Ruby code. But the first thing
Standard does is forces me to use colon syntax
instead of hashrockets, trailing commas at the end
of array and hash literals, putting space inside my blocks
and then using leading dots instead of trailing dots on method chains. So now I write Ruby like
this and I don't love it, but I value the greater consistency. So if you adopt Standard, our
hope is that you'll maintain consistency without
worrying so much or arguing about every little rule. And best of all, if you
don't like something, you can blame the tool or
me, instead of one another. (audience laughing) So the selfish programmer is fickle and opinions about unimportant
topics change over time. So sometimes it's better
just to rely on a tool to help you stay focused on the work. All right, the final stage,
the selfish programmer is unhelpful, they'd rather solve
problems once and for all, than spend time helping others
with the same issue over and over again, see, at
work developers are often too far away to help users. Customer support might sit
between them, and you know, supports the first responder to any kind of customer
issue, but often, you know, they don't know how to do
something, they might hand that off to developers,
the developers might look at it and you know, at
first glance, just kind of create a work-around and give it back and that'll be handed off to the users. But if the issue recurs,
support's gonna look at that and they've probably been trained that developer time is very
expensive and so the most likely thing is they're just gonna
repeat the work-around. And I've in the past seen
this happen where developers by being totally insulated
from user experiences will let very simple and easy-to-fix bugs just fester for months, and even years, because support has sort of just kind of created enough duct tape and rubber bands to keep everything working for customers. So that leads to problems
never really getting fixed. But if you're solo,
you're exposed everything, every email, every tweet, every error, and because I hate these notifications, I'm willing to spend lots
of time to make them stop. For example, I recently had a time bug, not caused by time zones or data types, but by the actual passage of time. You see, I had a controller
and it eventually grew to have two different purposes. So I split it up into two new controllers and then blew away the first one. Now, the moment that I deleted that file, even though nothing
else called it anymore, I suddenly got a ton of bug
reports and what I learned was that some people never
refresh their browser tabs. So they'd load the page,
I change the server, then their JavaScript
would keep calling it and, you know, create bugs. I can't believe this, but I was,
(audience laughing) I was still receiving
this exact bug report over six weeks later. (audience laughing) And so I didn't want to get
emails like this ever again. And so I needed some way
to update the client first and then leave plenty of
time for that JavaScript to propagate before I change
the server in a breaking way, But for a solo project,
how should I do that, you know, I could write
a little to-do comment for myself, like, "Delete this later." But, of course, that one was
because of LeBlanc's Law, which states that later equals never. So if I, yeah, I'm just going to forget about that comment and
then I'm going to have all this dead code sittin'
around on my system. So I spent a couple hours,
and I wrote a little gem called todo_or_die. It's a reference to an
NES game of the same name. And it's a single method,
which just takes a comment and a due date, so I
planned to delete this code by June 2019 and as time
passes, when June comes, if I run my server,
todo_or_die is gonna blow up, it's going to tell me
where to change my code. And in production, of course, I don't want to impact users, so todo_or_die will just log a warning instead. And now, even though, you know,
that gem took a little while to create, since I started
using it, I've never had a bug of the same category recur. So that's why the selfish
programmer is unhelpful, because they understand that
if nobody's bothering them, it must mean they did a good job. So that's just a little bit
about how the selfish programmer is antisocial, egotistical,
and irresponsible. And my hope is that
something here resonated with your work, so that you
can apply this selfishness to your own practice and craft. If you have any comments
or questions please tweet at me, send me a DM, or an email. That's my email address, justin@testdouble or @searls on Twitter. If you want to check out the
app that I was discussing today, it's KameSame.com. You can actually download 4k copies of all of the background artwork
from this presentation at that URL. Again, our company's Test Double. You might have seen Marla's
talk on remote work yesterday, you should definitely check
out Eric Weinstein's talk on interviewing tomorrow after lunch. And you know, if you're
interested in a career consulting or if your team's just
looking for additional senior developers, you can find
us at testdouble.com or on social things as @testdouble. And in closing, I hope this has
made you consider, you know, programming a bit more selfishly and I just want to thank
all of you from the bottom of my heart for for sharing
some of your time today. (audience applauding) (cheerful music)