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, you can email me.)
[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)
Explore our insights
New Stanford research shows 9.5% of engineers contribute almost no visible work—but blame won’t solve the problem. Here’s how leaders can fix the root cause.