Transcript

The video above was recorded at RailsConf 2019 conference in Minneapolis, Minnesota.

This presentation is an exploration of the things programmers can learn by building an application by and for themselves—and the surprising number of lessons that might translate to their work on teams and larger organizations. It was based on my experience building the Japanese-learning site KameSame as a companion app to WaniKani.

If you enjoy the talk, please share it with your friends and colleagues! And if you know a team that could use additional developers and is looking to improve, we’d love to hear from you. (If you have any other hot takes about this topic that you’d like to share, mentioning me on twitter dot com is the surest way to get my attention.)

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

Justin Searls

Hash An icon of a hash sign Code Name
Agent 002
Location An icon of a map marker Location
Columbus, OH